/* Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include "hfi_packetization.h" #include "msm_vidc_debug.h" #include "q6_hfi.h" #include "vidc_hfi_api.h" static struct hal_device_data hal_ctxt; static int write_queue(void *info, u8 *packet) { u32 packet_size_in_words, new_write_idx; struct q6_iface_q_info *qinfo; u32 empty_space, read_idx; u32 *write_ptr; if (!info || !packet) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } qinfo = (struct q6_iface_q_info *) info; packet_size_in_words = (*(u32 *)packet) >> 2; if (packet_size_in_words == 0) { dprintk(VIDC_ERR, "Zero packet size"); return -ENODATA; } read_idx = qinfo->read_idx; empty_space = (qinfo->write_idx >= read_idx) ? (qinfo->q_size - (qinfo->write_idx - read_idx)) : (read_idx - qinfo->write_idx); if (empty_space <= packet_size_in_words) { dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)", empty_space, packet_size_in_words); return -ENOTEMPTY; } new_write_idx = (qinfo->write_idx + packet_size_in_words); write_ptr = (u32 *)(qinfo->buffer + (qinfo->write_idx << 2)); if (new_write_idx < qinfo->q_size) { memcpy(write_ptr, packet, packet_size_in_words << 2); } else { new_write_idx -= qinfo->q_size; memcpy(write_ptr, packet, (packet_size_in_words - new_write_idx) << 2); memcpy((void *)qinfo->buffer, packet + ((packet_size_in_words - new_write_idx) << 2), new_write_idx << 2); } qinfo->write_idx = new_write_idx; return 0; } static int read_queue(void *info, u8 *packet) { u32 packet_size_in_words, new_read_idx; u32 *read_ptr; struct q6_iface_q_info *qinfo; if (!info || !packet) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } qinfo = (struct q6_iface_q_info *) info; if (qinfo->read_idx == qinfo->write_idx) return -EPERM; read_ptr = (u32 *)(qinfo->buffer + (qinfo->read_idx << 2)); packet_size_in_words = (*read_ptr) >> 2; if (packet_size_in_words == 0) { dprintk(VIDC_ERR, "Zero packet size"); return -ENODATA; } new_read_idx = qinfo->read_idx + packet_size_in_words; if (new_read_idx < qinfo->q_size) { memcpy(packet, read_ptr, packet_size_in_words << 2); } else { new_read_idx -= qinfo->q_size; memcpy(packet, read_ptr, (packet_size_in_words - new_read_idx) << 2); memcpy(packet + ((packet_size_in_words - new_read_idx) << 2), (u8 *)qinfo->buffer, new_read_idx << 2); } qinfo->read_idx = new_read_idx; return 0; } static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt) { struct q6_iface_q_info *q_info; int rc = 0; unsigned long flags = 0; if (!device || !pkt) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } q_info = &device->event_queue; if (!q_info->buffer) { dprintk(VIDC_ERR, "cannot write to shared Q"); rc = -ENODATA; goto err_q_write; } spin_lock_irqsave(&q_info->lock, flags); rc = write_queue(q_info, (u8 *)pkt); if (rc) dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full"); spin_unlock_irqrestore(&q_info->lock, flags); err_q_write: return rc; } static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt) { int rc = 0; struct q6_iface_q_info *q_info; unsigned long flags = 0; if (!pkt) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } q_info = &device->event_queue; if (!q_info->buffer) { dprintk(VIDC_ERR, "cannot read from shared Q"); rc = -ENODATA; goto read_error; } spin_lock_irqsave(&q_info->lock, flags); rc = read_queue(q_info, (u8 *)pkt); if (rc) { dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty"); rc = -ENODATA; } spin_unlock_irqrestore(&q_info->lock, flags); read_error: return rc; } static void q6_hfi_core_work_handler(struct work_struct *work) { int rc = 0; struct q6_hfi_device *device = container_of( work, struct q6_hfi_device, vidc_worker); u8 packet[VIDC_IFACEQ_MED_PKT_SIZE]; /* need to consume all the messages from the firmware */ do { rc = q6_hfi_iface_eventq_read(device, packet); if (!rc) hfi_process_msg_packet(device->callback, device->device_id, (struct vidc_hal_msg_pkt_hdr *) packet); } while (!rc); if (rc != -ENODATA) dprintk(VIDC_ERR, "Failed to read from event queue"); } static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device) { struct iommu_domain *domain; int rc = 0, i = 0; struct iommu_set *iommu_group_set; struct iommu_info *iommu_map; if (!device || !device->res) { dprintk(VIDC_ERR, "Invalid parameter: %p", device); return -EINVAL; } iommu_group_set = &device->res->iommu_group_set; for (i = 0; i < iommu_group_set->count; i++) { iommu_map = &iommu_group_set->iommu_maps[i]; iommu_map->group = iommu_group_find(iommu_map->name); if (!iommu_map->group) { dprintk(VIDC_ERR, "Failed to find group :%s\n", iommu_map->name); goto fail_group; } domain = iommu_group_get_iommudata(iommu_map->group); if (IS_ERR_OR_NULL(domain)) { dprintk(VIDC_ERR, "Failed to get domain data for group %p", iommu_map->group); goto fail_group; } iommu_map->domain = msm_find_domain_no(domain); if (iommu_map->domain < 0) { dprintk(VIDC_ERR, "Failed to get domain index for domain %p", domain); goto fail_group; } } return rc; fail_group: for (--i; i >= 0; i--) { iommu_map = &iommu_group_set->iommu_maps[i]; if (iommu_map->group) iommu_group_put(iommu_map->group); iommu_map->group = NULL; iommu_map->domain = -1; } return -EINVAL; } static void q6_hfi_deregister_iommu_domains(struct q6_hfi_device *device) { struct iommu_set *iommu_group_set; struct iommu_info *iommu_map; int i = 0; if (!device || !device->res) { dprintk(VIDC_ERR, "Invalid parameter: %p", device); return; } iommu_group_set = &device->res->iommu_group_set; for (i = 0; i < iommu_group_set->count; i++) { iommu_map = &iommu_group_set->iommu_maps[i]; if (iommu_map->group) iommu_group_put(iommu_map->group); iommu_map->group = NULL; iommu_map->domain = -1; } } static int q6_hfi_init_resources(struct q6_hfi_device *device, struct msm_vidc_platform_resources *res) { int rc = 0; if (!device || !res) { dprintk(VIDC_ERR, "Invalid device or resources"); return -EINVAL; } device->res = res; rc = q6_hfi_register_iommu_domains(device); if (rc) dprintk(VIDC_ERR, "Failed to register iommu domains: %d\n", rc); return rc; } static void q6_hfi_deinit_resources(struct q6_hfi_device *device) { q6_hfi_deregister_iommu_domains(device); } static void *q6_hfi_add_device(u32 device_id, hfi_cmd_response_callback callback) { struct q6_hfi_device *hdevice = NULL; if (!callback) { dprintk(VIDC_ERR, "Invalid Paramters"); return NULL; } hdevice = (struct q6_hfi_device *) kzalloc(sizeof(struct q6_hfi_device), GFP_KERNEL); if (!hdevice) { dprintk(VIDC_ERR, "failed to allocate new device"); goto err_alloc; } hdevice->device_id = device_id; hdevice->callback = callback; dprintk(VIDC_DBG, "q6_hfi_add_device device_id %d\n", device_id); INIT_WORK(&hdevice->vidc_worker, q6_hfi_core_work_handler); hdevice->vidc_workq = create_singlethread_workqueue( "msm_vidc_workerq_q6"); if (!hdevice->vidc_workq) { dprintk(VIDC_ERR, ": create workq failed\n"); goto error_createq; } if (hal_ctxt.dev_count == 0) INIT_LIST_HEAD(&hal_ctxt.dev_head); INIT_LIST_HEAD(&hdevice->list); list_add_tail(&hdevice->list, &hal_ctxt.dev_head); hal_ctxt.dev_count++; return (void *) hdevice; error_createq: kfree(hdevice); err_alloc: return NULL; } static void *q6_hfi_get_device(u32 device_id, struct msm_vidc_platform_resources *res, hfi_cmd_response_callback callback) { struct q6_hfi_device *device; int rc = 0; if (!callback) { dprintk(VIDC_ERR, "%s Invalid params: %p\n", __func__, callback); return NULL; } device = q6_hfi_add_device(device_id, &handle_cmd_response); if (!device) { dprintk(VIDC_ERR, "Failed to create HFI device\n"); return NULL; } rc = q6_hfi_init_resources(device, res); if (rc) { dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc); goto err_fail_init_res; } return device; err_fail_init_res: q6_hfi_delete_device(device); return NULL; } void q6_hfi_delete_device(void *device) { struct q6_hfi_device *close, *tmp, *dev; if (device) { q6_hfi_deinit_resources(device); dev = (struct q6_hfi_device *) device; list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) { if (close->device_id == dev->device_id) { hal_ctxt.dev_count--; list_del(&close->list); destroy_workqueue(close->vidc_workq); kfree(close); break; } } } } static inline void q6_hfi_add_apr_hdr(struct q6_hfi_device *dev, struct apr_hdr *hdr, u32 pkt_size, u32 opcode) { hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(sizeof(struct apr_hdr)), APR_PKT_VER); hdr->src_svc = ((struct apr_svc *)dev->apr)->id; hdr->src_domain = APR_DOMAIN_APPS; hdr->dest_svc = APR_SVC_VIDC; hdr->src_port = 0; hdr->dest_port = 0; hdr->pkt_size = pkt_size; hdr->token = 0; hdr->opcode = opcode; } static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv) { struct q6_hfi_device *device = priv; int rc = 0; if (!data || !device || !data->payload_size) { dprintk(VIDC_ERR, "%s - Invalid arguments", __func__); return -EINVAL; } dprintk(VIDC_DBG, "%s opcode = %u payload size = %u", __func__, data->opcode, data->payload_size); rc = q6_hfi_iface_eventq_write(device, data->payload); if (rc) { dprintk(VIDC_ERR, "%s failed to write to event queue", __func__); return rc; } queue_work(device->vidc_workq, &device->vidc_worker); return 0; } static void q6_release_event_queue(struct q6_hfi_device *device) { kfree(device->event_queue.buffer); device->event_queue.buffer = NULL; device->event_queue.q_size = 0; device->event_queue.read_idx = 0; device->event_queue.write_idx = 0; } static int q6_init_event_queue(struct q6_hfi_device *dev) { struct q6_iface_q_info *iface_q; if (!dev) { dprintk(VIDC_ERR, "Invalid device"); return -EINVAL; } iface_q = &dev->event_queue; iface_q->buffer = kzalloc(Q6_IFACEQ_QUEUE_SIZE, GFP_KERNEL); if (!iface_q->buffer) { dprintk(VIDC_ERR, "iface_q alloc failed"); q6_release_event_queue(dev); return -ENOMEM; } else { iface_q->q_size = Q6_IFACEQ_QUEUE_SIZE / 4; iface_q->read_idx = 0; iface_q->write_idx = 0; spin_lock_init(&iface_q->lock); } return 0; } static int q6_hfi_core_init(void *device) { struct q6_apr_cmd_sys_init_packet apr; int rc = 0; struct q6_hfi_device *dev = device; if (!dev) { dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); return -ENODEV; } INIT_LIST_HEAD(&dev->sess_head); if (!dev->event_queue.buffer) { rc = q6_init_event_queue(dev); if (rc) { dprintk(VIDC_ERR, "q6_init_event_queue failed"); goto err_core_init; } } else { dprintk(VIDC_ERR, "queue buffer exists"); rc = -EEXIST; goto err_core_init; } q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SYS_INIT); rc = create_pkt_cmd_sys_init(&apr.pkt, HFI_VIDEO_ARCH_OX); if (rc) { dprintk(VIDC_ERR, "Failed to create sys init pkt"); goto err_core_init; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_core_init: return rc; } static int q6_hfi_core_release(void *device) { struct q6_hfi_device *dev = device; if (!dev) { dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); return -ENODEV; } q6_release_event_queue(dev); dprintk(VIDC_DBG, "HAL exited\n"); return 0; } static int q6_hfi_core_pc_prep(void *device) { (void) device; /* Q6 does not support core_pc_prep*/ return 0; } static int q6_hfi_core_ping(void *device) { (void) device; /* Q6 does not support cmd_sys_ping */ return 0; } static void *q6_hfi_session_init(void *device, u32 session_id, enum hal_domain session_type, enum hal_video_codec codec_type) { struct q6_apr_cmd_sys_session_init_packet apr; struct hal_session *new_session; struct q6_hfi_device *dev = device; int rc = 0; if (!dev) { dprintk(VIDC_ERR, "%s: invalid argument\n", __func__); return NULL; } new_session = (struct hal_session *) kzalloc(sizeof(struct hal_session), GFP_KERNEL); new_session->session_id = (u32) session_id; if (session_type == 1) new_session->is_decoder = 0; else if (session_type == 2) new_session->is_decoder = 1; new_session->device = dev; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SYS_SESSION_INIT); if (create_pkt_cmd_sys_session_init(&apr.pkt, (u32)new_session, session_type, codec_type)) { dprintk(VIDC_ERR, "session_init: failed to create packet"); goto err_session_init; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; goto err_session_init; } list_add_tail(&new_session->list, &dev->sess_head); return new_session; err_session_init: kfree(new_session); return NULL; } static int q6_hal_send_session_cmd(void *sess, int pkt_type) { struct q6_apr_session_cmd_pkt apr; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !session->device) { dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); return -EINVAL; } dev = session->device; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), pkt_type); rc = create_pkt_cmd_session_cmd(&apr.pkt, pkt_type, (u32)session); if (rc) { dprintk(VIDC_ERR, "send session cmd: create pkt failed"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_end(void *session) { return q6_hal_send_session_cmd(session, HFI_CMD_SYS_SESSION_END); } static int q6_hfi_session_abort(void *session) { return q6_hal_send_session_cmd(session, HFI_CMD_SYS_SESSION_ABORT); } static int q6_hfi_session_clean(void *session) { struct hal_session *sess_close; if (!session) { dprintk(VIDC_ERR, "Invalid Params %s", __func__); return -EINVAL; } sess_close = session; dprintk(VIDC_DBG, "deleted the session: 0x%x", sess_close->session_id); list_del(&sess_close->list); kfree(sess_close); return 0; } static int q6_hfi_session_set_buffers(void *sess, struct vidc_buffer_addr_info *buffer_info) { struct q6_apr_cmd_session_set_buffers_packet *apr; u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !buffer_info || !session->device) { dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); return -EINVAL; } dev = session->device; if (buffer_info->buffer_type == HAL_BUFFER_INPUT) return 0; apr = (struct q6_apr_cmd_session_set_buffers_packet *)packet; q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE, HFI_CMD_SESSION_SET_BUFFERS); rc = create_pkt_cmd_session_set_buffers(&apr->pkt, (u32)session, buffer_info); if (rc) { dprintk(VIDC_ERR, "set buffers: failed to create packet"); goto err_create_pkt; } dprintk(VIDC_INFO, "set buffers: 0x%x", buffer_info->buffer_type); rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_release_buffers(void *sess, struct vidc_buffer_addr_info *buffer_info) { struct q6_apr_cmd_session_release_buffer_packet *apr; u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !buffer_info || !session->device) { dprintk(VIDC_ERR, "%s: invalid arguments\n", __func__); return -EINVAL; } dev = session->device; if (buffer_info->buffer_type == HAL_BUFFER_INPUT) return 0; apr = (struct q6_apr_cmd_session_release_buffer_packet *) packet; q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE, HFI_CMD_SESSION_RELEASE_BUFFERS); rc = create_pkt_cmd_session_release_buffers(&apr->pkt, (u32)session, buffer_info); if (rc) { dprintk(VIDC_ERR, "release buffers: failed to create packet"); goto err_create_pkt; } dprintk(VIDC_INFO, "Release buffers: 0x%x", buffer_info->buffer_type); rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_load_res(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_LOAD_RESOURCES); } static int q6_hfi_session_release_res(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_RELEASE_RESOURCES); } static int q6_hfi_session_start(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_START); } static int q6_hfi_session_stop(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_STOP); } static int q6_hfi_session_suspend(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_SUSPEND); } static int q6_hfi_session_resume(void *sess) { return q6_hal_send_session_cmd(sess, HFI_CMD_SESSION_RESUME); } static int q6_hfi_session_etb(void *sess, struct vidc_frame_data *input_frame) { int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !input_frame || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; if (session->is_decoder) { struct q6_apr_cmd_session_empty_buffer_compressed_packet apr; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SESSION_EMPTY_BUFFER); rc = create_pkt_cmd_session_etb_decoder(&apr.pkt, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, "Session etb decoder: failed to create pkt"); goto err_create_pkt; } dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER"); dprintk(VIDC_DBG, "addr = 0x%x ts = %lld", input_frame->device_addr, input_frame->timestamp); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; } else { struct q6_apr_cmd_session_empty_buffer_uncompressed_plane0_packet apr; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SESSION_EMPTY_BUFFER); rc = create_pkt_cmd_session_etb_encoder(&apr.pkt, (u32)session, input_frame); if (rc) { dprintk(VIDC_ERR, "Session etb encoder: failed to create pkt"); goto err_create_pkt; } dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER"); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; } err_create_pkt: return rc; } static int q6_hfi_session_ftb(void *sess, struct vidc_frame_data *output_frame) { struct q6_apr_cmd_session_fill_buffer_packet apr; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !output_frame || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SESSION_FILL_BUFFER); rc = create_pkt_cmd_session_ftb(&apr.pkt, (u32)session, output_frame); if (rc) { dprintk(VIDC_ERR, "Session ftb: failed to create pkt"); goto err_create_pkt; } dprintk(VIDC_INFO, "Q OUTPUT BUFFER"); rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_parse_seq_hdr(void *sess, struct vidc_seq_hdr *seq_hdr) { struct q6_apr_cmd_session_parse_sequence_header_packet *apr; int rc = 0; u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !seq_hdr || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; apr = (struct q6_apr_cmd_session_parse_sequence_header_packet *) packet; q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_SMALL_PKT_SIZE, HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER); rc = create_pkt_cmd_session_parse_seq_header(&apr->pkt, (u32)session, seq_hdr); if (rc) { dprintk(VIDC_ERR, "Session parse seq hdr: failed to create pkt"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_get_seq_hdr(void *sess, struct vidc_seq_hdr *seq_hdr) { struct q6_apr_cmd_session_get_sequence_header_packet *apr; int rc = 0; u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE]; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !seq_hdr || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; apr = (struct q6_apr_cmd_session_get_sequence_header_packet *) packet; q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_SMALL_PKT_SIZE, HFI_CMD_SESSION_GET_SEQUENCE_HEADER); rc = create_pkt_cmd_session_get_seq_hdr(&apr->pkt, (u32)session, seq_hdr); if (rc) { dprintk(VIDC_ERR, "Session get seq hdr: failed to create pkt"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_get_buf_req(void *sess) { struct q6_apr_cmd_session_get_property_packet apr; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SESSION_GET_PROPERTY); rc = create_pkt_cmd_session_get_buf_req(&apr.pkt, (u32)session); if (rc) { dprintk(VIDC_ERR, "Session get buf req: failed to create pkt"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode) { struct q6_apr_cmd_session_flush_packet apr; int rc = 0; struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; q6_hfi_add_apr_hdr(dev, &apr.hdr, sizeof(apr), HFI_CMD_SESSION_FLUSH); rc = create_pkt_cmd_session_flush(&apr.pkt, (u32)session, flush_mode); if (rc) { dprintk(VIDC_ERR, "Session flush: failed to create pkt"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)&apr); if (rc != apr.hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_set_property(void *sess, enum hal_property ptype, void *pdata) { u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE]; struct q6_apr_cmd_session_set_property_packet *apr = (struct q6_apr_cmd_session_set_property_packet *) &packet; struct hal_session *session = sess; int rc = 0; struct q6_hfi_device *dev; if (!session || !pdata || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; dprintk(VIDC_DBG, "in set_prop,with prop id: 0x%x", ptype); q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE, HFI_CMD_SESSION_SET_PROPERTY); rc = create_pkt_cmd_session_set_property(&apr->pkt, (u32)session, ptype, pdata); if (rc) { dprintk(VIDC_ERR, "set property: failed to create packet"); goto err_create_pkt; } rc = apr_send_pkt(dev->apr, (uint32_t *)apr); if (rc != apr->hdr.pkt_size) { dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d", __func__, rc); rc = -EBADE; } else rc = 0; err_create_pkt: return rc; } static int q6_hfi_session_get_property(void *sess, enum hal_property ptype, void *pdata) { struct hal_session *session = sess; struct q6_hfi_device *dev; if (!session || !pdata || !session->device) { dprintk(VIDC_ERR, "Invalid Params"); return -EINVAL; } dev = session->device; dprintk(VIDC_DBG, "IN func: , with property id: %d", ptype); switch (ptype) { case HAL_CONFIG_FRAME_RATE: break; case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT: break; case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: break; case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: break; case HAL_PARAM_EXTRA_DATA_HEADER_CONFIG: break; case HAL_PARAM_FRAME_SIZE: break; case HAL_CONFIG_REALTIME: break; case HAL_PARAM_BUFFER_COUNT_ACTUAL: break; case HAL_PARAM_NAL_STREAM_FORMAT_SELECT: break; case HAL_PARAM_VDEC_OUTPUT_ORDER: break; case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE: break; case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO: break; case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER: break; case HAL_PARAM_VDEC_MULTI_STREAM: break; case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT: break; case HAL_PARAM_DIVX_FORMAT: break; case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING: break; case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER: break; case HAL_CONFIG_VDEC_MB_ERROR_MAP: break; case HAL_CONFIG_VENC_REQUEST_IFRAME: break; case HAL_PARAM_VENC_MPEG4_SHORT_HEADER: break; case HAL_PARAM_VENC_MPEG4_AC_PREDICTION: break; case HAL_CONFIG_VENC_TARGET_BITRATE: break; case HAL_PARAM_PROFILE_LEVEL_CURRENT: break; case HAL_PARAM_VENC_H264_ENTROPY_CONTROL: break; case HAL_PARAM_VENC_RATE_CONTROL: break; case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION: break; case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION: break; case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL: break; case HAL_PARAM_VENC_SESSION_QP: break; case HAL_CONFIG_VENC_INTRA_PERIOD: break; case HAL_CONFIG_VENC_IDR_PERIOD: break; case HAL_CONFIG_VPE_OPERATIONS: break; case HAL_PARAM_VENC_INTRA_REFRESH: break; case HAL_PARAM_VENC_MULTI_SLICE_CONTROL: break; case HAL_CONFIG_VPE_DEINTERLACE: break; case HAL_SYS_DEBUG_CONFIG: break; /*FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET*/ case HAL_CONFIG_BUFFER_REQUIREMENTS: case HAL_CONFIG_PRIORITY: case HAL_CONFIG_BATCH_INFO: case HAL_PARAM_METADATA_PASS_THROUGH: case HAL_SYS_IDLE_INDICATOR: case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED: case HAL_PARAM_CHROMA_SITE: case HAL_PARAM_PROPERTIES_SUPPORTED: case HAL_PARAM_PROFILE_LEVEL_SUPPORTED: case HAL_PARAM_CAPABILITY_SUPPORTED: case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED: case HAL_PARAM_MULTI_VIEW_FORMAT: case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE: case HAL_PARAM_CODEC_SUPPORTED: case HAL_PARAM_VDEC_MULTI_VIEW_SELECT: case HAL_PARAM_VDEC_MB_QUANTIZATION: case HAL_PARAM_VDEC_NUM_CONCEALED_MB: case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING: case HAL_PARAM_VENC_SLICE_DELIVERY_MODE: case HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING: case HAL_CONFIG_BUFFER_COUNT_ACTUAL: case HAL_CONFIG_VDEC_MULTI_STREAM: case HAL_PARAM_VENC_MULTI_SLICE_INFO: case HAL_CONFIG_VENC_TIMESTAMP_SCALE: case HAL_PARAM_VENC_LOW_LATENCY: default: dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x", ptype); break; } return 0; } static int q6_hfi_scale_clocks(void *dev, int load) { (void)dev; (void)load; /* Q6 does not support clocks scaling */ return 0; } static int q6_hfi_scale_bus(void *dev, int load, enum session_type type, enum mem_type mtype) { (void)dev; (void)load; (void)type; (void)mtype; /* Q6 does not support bus scaling */ return 0; } static int q6_hfi_unset_ocmem(void *dev) { (void)dev; /* Q6 does not support ocmem */ return -EINVAL; } static int q6_hfi_alloc_ocmem(void *dev, unsigned long size) { (void)dev; (void)size; /* Q6 does not support ocmem */ return 0; } static int q6_hfi_free_ocmem(void *dev) { (void)dev; /* Q6 does not support ocmem */ return 0; } static int q6_hfi_iommu_get_domain_partition(void *dev, u32 flags, u32 buffer_type, int *domain, int *partition) { (void)dev; dprintk(VIDC_ERR, "Not implemented: %s", __func__); return -ENOTSUPP; } static int q6_hfi_iommu_attach(struct q6_hfi_device *device) { int rc = 0; struct iommu_domain *domain; int i; struct iommu_set *iommu_group_set; struct iommu_group *group; struct iommu_info *iommu_map; if (!device || !device->res) { dprintk(VIDC_ERR, "Invalid parameter: %p", device); return -EINVAL; } iommu_group_set = &device->res->iommu_group_set; for (i = 0; i < iommu_group_set->count; i++) { iommu_map = &iommu_group_set->iommu_maps[i]; group = iommu_map->group; domain = msm_get_iommu_domain(iommu_map->domain); if (IS_ERR_OR_NULL(domain)) { dprintk(VIDC_ERR, "Failed to get domain: %s", iommu_map->name); rc = IS_ERR(domain) ? PTR_ERR(domain) : -EINVAL; break; } dprintk(VIDC_DBG, "Attaching domain(id:%d) %p to group %p", iommu_map->domain, domain, group); rc = iommu_attach_group(domain, group); if (rc) { dprintk(VIDC_ERR, "IOMMU attach failed: %s", iommu_map->name); break; } } if (i < iommu_group_set->count) { i--; for (; i >= 0; i--) { iommu_map = &iommu_group_set->iommu_maps[i]; group = iommu_map->group; domain = msm_get_iommu_domain(iommu_map->domain); if (group && domain) iommu_detach_group(domain, group); } } return rc; } static void q6_hfi_iommu_detach(struct q6_hfi_device *device) { struct iommu_group *group; struct iommu_domain *domain; struct iommu_set *iommu_group_set; struct iommu_info *iommu_map; int i; if (!device || !device->res) { dprintk(VIDC_ERR, "Invalid parameter: %p", device); return; } iommu_group_set = &device->res->iommu_group_set; for (i = 0; i < iommu_group_set->count; i++) { iommu_map = &iommu_group_set->iommu_maps[i]; group = iommu_map->group; domain = msm_get_iommu_domain(iommu_map->domain); if (group && domain) iommu_detach_group(domain, group); } } static int q6_hfi_load_fw(void *dev) { int rc = 0; struct q6_hfi_device *device = dev; if (!device) return -EINVAL; if (!device->resources.fw.cookie) device->resources.fw.cookie = subsystem_get("adsp"); if (IS_ERR_OR_NULL(device->resources.fw.cookie)) { dprintk(VIDC_ERR, "Failed to download firmware\n"); rc = -ENOMEM; goto fail_subsystem_get; } /*Set Q6 to loaded state*/ apr_set_q6_state(APR_SUBSYS_LOADED); device->apr = apr_register("ADSP", "VIDC", (apr_fn)q6_hfi_apr_callback, 0xFFFFFFFF, device); if (device->apr == NULL) { dprintk(VIDC_ERR, "Failed to register with QDSP6"); rc = -EINVAL; goto fail_apr_register; } rc = q6_hfi_iommu_attach(device); if (rc) { dprintk(VIDC_ERR, "Failed to attach iommu"); goto fail_iommu_attach; } return rc; fail_iommu_attach: apr_deregister(device->apr); device->apr = NULL; fail_apr_register: subsystem_put(device->resources.fw.cookie); device->resources.fw.cookie = NULL; fail_subsystem_get: return rc; } static void q6_hfi_unload_fw(void *hfi_device_data) { struct q6_hfi_device *device = hfi_device_data; if (!device) return; if (device->resources.fw.cookie) { q6_hfi_iommu_detach(device); subsystem_put(device->resources.fw.cookie); device->resources.fw.cookie = NULL; } if (device->apr) { if (apr_deregister(device->apr)) dprintk(VIDC_ERR, "Failed to deregister APR"); device->apr = NULL; } } static int q6_hfi_get_fw_info(void *dev, enum fw_info info) { (void)dev; (void)info; return 0; } static int q6_hfi_get_stride_scanline(int color_fmt, int width, int height, int *stride, int *scanlines) { *stride = VENUS_Y_STRIDE(color_fmt, width); *scanlines = VENUS_Y_SCANLINES(color_fmt, height); return 0; } static void q6_init_hfi_callbacks(struct hfi_device *hdev) { hdev->core_init = q6_hfi_core_init; hdev->core_release = q6_hfi_core_release; hdev->core_pc_prep = q6_hfi_core_pc_prep; hdev->core_ping = q6_hfi_core_ping; hdev->session_init = q6_hfi_session_init; hdev->session_end = q6_hfi_session_end; hdev->session_abort = q6_hfi_session_abort; hdev->session_clean = q6_hfi_session_clean; hdev->session_set_buffers = q6_hfi_session_set_buffers; hdev->session_release_buffers = q6_hfi_session_release_buffers; hdev->session_load_res = q6_hfi_session_load_res; hdev->session_release_res = q6_hfi_session_release_res; hdev->session_start = q6_hfi_session_start; hdev->session_stop = q6_hfi_session_stop; hdev->session_suspend = q6_hfi_session_suspend; hdev->session_resume = q6_hfi_session_resume; hdev->session_etb = q6_hfi_session_etb; hdev->session_ftb = q6_hfi_session_ftb; hdev->session_parse_seq_hdr = q6_hfi_session_parse_seq_hdr; hdev->session_get_seq_hdr = q6_hfi_session_get_seq_hdr; hdev->session_get_buf_req = q6_hfi_session_get_buf_req; hdev->session_flush = q6_hfi_session_flush; hdev->session_set_property = q6_hfi_session_set_property; hdev->session_get_property = q6_hfi_session_get_property; hdev->scale_clocks = q6_hfi_scale_clocks; hdev->scale_bus = q6_hfi_scale_bus; hdev->unset_ocmem = q6_hfi_unset_ocmem; hdev->alloc_ocmem = q6_hfi_alloc_ocmem; hdev->free_ocmem = q6_hfi_free_ocmem; hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition; hdev->load_fw = q6_hfi_load_fw; hdev->unload_fw = q6_hfi_unload_fw; hdev->get_fw_info = q6_hfi_get_fw_info; hdev->get_stride_scanline = q6_hfi_get_stride_scanline; } int q6_hfi_initialize(struct hfi_device *hdev, u32 device_id, struct msm_vidc_platform_resources *res, hfi_cmd_response_callback callback) { int rc = 0; if (!hdev || !res || !callback) { dprintk(VIDC_ERR, "Invalid params: %p %p %p", hdev, res, callback); rc = -EINVAL; goto err_hfi_init; } hdev->hfi_device_data = q6_hfi_get_device(device_id, res, callback); q6_init_hfi_callbacks(hdev); err_hfi_init: return rc; }