Vinay Kalia | 3766b1e | 2012-01-11 18:58:41 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2012, Code Aurora Forum. All rights reserved. |
| 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | #include <linux/slab.h> |
| 15 | #include <media/msm_vidc.h> |
| 16 | #include "msm_vidc_internal.h" |
| 17 | #include "msm_vdec.h" |
| 18 | #include "msm_venc.h" |
| 19 | #include "msm_vidc_common.h" |
| 20 | #include "msm_smem.h" |
| 21 | |
| 22 | int msm_vidc_poll(void *instance, struct file *filp, |
| 23 | struct poll_table_struct *wait) |
| 24 | { |
| 25 | int rc = 0; |
| 26 | struct msm_vidc_inst *inst = instance; |
| 27 | struct vb2_queue *outq = &inst->vb2_bufq[OUTPUT_PORT]; |
| 28 | struct vb2_queue *capq = &inst->vb2_bufq[CAPTURE_PORT]; |
| 29 | struct vb2_buffer *out_vb = NULL; |
| 30 | struct vb2_buffer *cap_vb = NULL; |
| 31 | unsigned long flags; |
| 32 | if (!outq->streaming && !capq->streaming) { |
| 33 | pr_err("Returning POLLERR from here: %d, %d\n", |
| 34 | outq->streaming, capq->streaming); |
| 35 | return POLLERR; |
| 36 | } |
| 37 | poll_wait(filp, &capq->done_wq, wait); |
| 38 | poll_wait(filp, &outq->done_wq, wait); |
| 39 | spin_lock_irqsave(&capq->done_lock, flags); |
| 40 | if (!list_empty(&capq->done_list)) |
| 41 | cap_vb = list_first_entry(&capq->done_list, struct vb2_buffer, |
| 42 | done_entry); |
| 43 | if (cap_vb && (cap_vb->state == VB2_BUF_STATE_DONE |
| 44 | || cap_vb->state == VB2_BUF_STATE_ERROR)) |
| 45 | rc |= POLLIN | POLLRDNORM; |
| 46 | spin_unlock_irqrestore(&capq->done_lock, flags); |
| 47 | spin_lock_irqsave(&outq->done_lock, flags); |
| 48 | if (!list_empty(&outq->done_list)) |
| 49 | out_vb = list_first_entry(&outq->done_list, struct vb2_buffer, |
| 50 | done_entry); |
| 51 | if (out_vb && (out_vb->state == VB2_BUF_STATE_DONE |
| 52 | || out_vb->state == VB2_BUF_STATE_ERROR)) |
| 53 | rc |= POLLOUT | POLLWRNORM; |
| 54 | spin_unlock_irqrestore(&outq->done_lock, flags); |
| 55 | return rc; |
| 56 | } |
| 57 | |
| 58 | int msm_vidc_querycap(void *instance, struct v4l2_capability *cap) |
| 59 | { |
| 60 | struct msm_vidc_inst *inst = instance; |
| 61 | if (inst->session_type == MSM_VIDC_DECODER) |
| 62 | return msm_vdec_querycap(instance, cap); |
| 63 | else if (inst->session_type == MSM_VIDC_ENCODER) |
| 64 | return msm_venc_querycap(instance, cap); |
| 65 | return -EINVAL; |
| 66 | } |
| 67 | int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f) |
| 68 | { |
| 69 | struct msm_vidc_inst *inst = instance; |
| 70 | if (inst->session_type == MSM_VIDC_DECODER) |
| 71 | return msm_vdec_enum_fmt(instance, f); |
| 72 | else if (inst->session_type == MSM_VIDC_ENCODER) |
| 73 | return msm_venc_enum_fmt(instance, f); |
| 74 | return -EINVAL; |
| 75 | } |
| 76 | int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) |
| 77 | { |
| 78 | struct msm_vidc_inst *inst = instance; |
| 79 | if (inst->session_type == MSM_VIDC_DECODER) |
| 80 | return msm_vdec_s_fmt(instance, f); |
| 81 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 82 | return msm_venc_s_fmt(instance, f); |
| 83 | return -EINVAL; |
| 84 | } |
| 85 | int msm_vidc_g_fmt(void *instance, struct v4l2_format *f) |
| 86 | { |
| 87 | struct msm_vidc_inst *inst = instance; |
| 88 | if (inst->session_type == MSM_VIDC_DECODER) |
| 89 | return msm_vdec_g_fmt(instance, f); |
| 90 | else if (inst->session_type == MSM_VIDC_ENCODER) |
| 91 | return msm_venc_g_fmt(instance, f); |
| 92 | return -EINVAL; |
| 93 | } |
| 94 | int msm_vidc_s_ctrl(void *instance, struct v4l2_control *control) |
| 95 | { |
| 96 | struct msm_vidc_inst *inst = instance; |
| 97 | if (inst->session_type == MSM_VIDC_DECODER) |
| 98 | return msm_vdec_s_ctrl(instance, control); |
Ashray Kulkarni | d2ab0e3 | 2012-04-03 18:40:29 -0700 | [diff] [blame^] | 99 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 100 | return msm_venc_s_ctrl(instance, control); |
Vinay Kalia | 3766b1e | 2012-01-11 18:58:41 -0800 | [diff] [blame] | 101 | return -EINVAL; |
| 102 | } |
| 103 | int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control) |
| 104 | { |
| 105 | struct msm_vidc_inst *inst = instance; |
| 106 | if (inst->session_type == MSM_VIDC_DECODER) |
| 107 | return msm_vdec_g_ctrl(instance, control); |
Ashray Kulkarni | d2ab0e3 | 2012-04-03 18:40:29 -0700 | [diff] [blame^] | 108 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 109 | return msm_venc_g_ctrl(instance, control); |
Vinay Kalia | 3766b1e | 2012-01-11 18:58:41 -0800 | [diff] [blame] | 110 | return -EINVAL; |
| 111 | } |
| 112 | int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b) |
| 113 | { |
| 114 | struct msm_vidc_inst *inst = instance; |
| 115 | if (inst->session_type == MSM_VIDC_DECODER) |
| 116 | return msm_vdec_reqbufs(instance, b); |
| 117 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 118 | return msm_venc_reqbufs(instance, b); |
| 119 | return -EINVAL; |
| 120 | } |
| 121 | |
| 122 | int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b) |
| 123 | { |
| 124 | struct msm_vidc_inst *inst = instance; |
| 125 | if (inst->session_type == MSM_VIDC_DECODER) |
| 126 | return msm_vdec_prepare_buf(instance, b); |
| 127 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 128 | return msm_venc_prepare_buf(instance, b); |
| 129 | return -EINVAL; |
| 130 | } |
| 131 | |
| 132 | int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) |
| 133 | { |
| 134 | struct msm_vidc_inst *inst = instance; |
| 135 | if (inst->session_type == MSM_VIDC_DECODER) |
| 136 | return msm_vdec_qbuf(instance, b); |
| 137 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 138 | return msm_venc_qbuf(instance, b); |
| 139 | return -EINVAL; |
| 140 | } |
| 141 | |
| 142 | int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) |
| 143 | { |
| 144 | struct msm_vidc_inst *inst = instance; |
| 145 | if (inst->session_type == MSM_VIDC_DECODER) |
| 146 | return msm_vdec_dqbuf(instance, b); |
| 147 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 148 | return msm_venc_dqbuf(instance, b); |
| 149 | return -EINVAL; |
| 150 | } |
| 151 | |
| 152 | int msm_vidc_streamon(void *instance, enum v4l2_buf_type i) |
| 153 | { |
| 154 | struct msm_vidc_inst *inst = instance; |
| 155 | if (inst->session_type == MSM_VIDC_DECODER) |
| 156 | return msm_vdec_streamon(instance, i); |
| 157 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 158 | return msm_venc_streamon(instance, i); |
| 159 | return -EINVAL; |
| 160 | } |
| 161 | |
| 162 | int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i) |
| 163 | { |
| 164 | struct msm_vidc_inst *inst = instance; |
| 165 | if (inst->session_type == MSM_VIDC_DECODER) |
| 166 | return msm_vdec_streamoff(instance, i); |
| 167 | if (inst->session_type == MSM_VIDC_ENCODER) |
| 168 | return msm_venc_streamoff(instance, i); |
| 169 | return -EINVAL; |
| 170 | } |
| 171 | |
| 172 | void *vidc_get_userptr(void *alloc_ctx, unsigned long vaddr, |
| 173 | unsigned long size, int write) |
| 174 | { |
| 175 | return NULL; |
| 176 | } |
| 177 | |
| 178 | void vidc_put_userptr(void *buf_priv) |
| 179 | { |
| 180 | } |
| 181 | |
| 182 | static const struct vb2_mem_ops msm_vidc_vb2_mem_ops = { |
| 183 | .get_userptr = vidc_get_userptr, |
| 184 | .put_userptr = vidc_put_userptr, |
| 185 | }; |
| 186 | |
| 187 | static inline int vb2_bufq_init(struct msm_vidc_inst *inst, |
| 188 | enum v4l2_buf_type type, enum session_type sess) |
| 189 | { |
| 190 | struct vb2_queue *q = NULL; |
| 191 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { |
| 192 | q = &inst->vb2_bufq[CAPTURE_PORT]; |
| 193 | } else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
| 194 | q = &inst->vb2_bufq[OUTPUT_PORT]; |
| 195 | } else { |
| 196 | pr_err("buf_type = %d not recognised\n", type); |
| 197 | return -EINVAL; |
| 198 | } |
| 199 | q->type = type; |
| 200 | q->io_modes = VB2_MMAP | VB2_USERPTR; |
| 201 | q->io_flags = 0; |
| 202 | if (sess == MSM_VIDC_DECODER) |
| 203 | q->ops = msm_vdec_get_vb2q_ops(); |
| 204 | else if (sess == MSM_VIDC_ENCODER) |
| 205 | q->ops = msm_venc_get_vb2q_ops(); |
| 206 | q->mem_ops = &msm_vidc_vb2_mem_ops; |
| 207 | q->drv_priv = inst; |
| 208 | return vb2_queue_init(q); |
| 209 | } |
| 210 | |
| 211 | void *msm_vidc_open(int core_id, int session_type) |
| 212 | { |
| 213 | struct msm_vidc_inst *inst = NULL; |
| 214 | struct msm_vidc_core *core = NULL; |
| 215 | unsigned long flags; |
| 216 | int rc = 0; |
| 217 | int i = 0; |
| 218 | if (core_id >= MSM_VIDC_CORES_MAX || |
| 219 | session_type >= MSM_VIDC_MAX_DEVICES) { |
| 220 | pr_err("Invalid input, core_id = %d, session = %d\n", |
| 221 | core_id, session_type); |
| 222 | goto err_invalid_core; |
| 223 | } |
| 224 | core = get_vidc_core(core_id); |
| 225 | if (!core) { |
| 226 | pr_err("Failed to find core for core_id = %d\n", core_id); |
| 227 | goto err_invalid_core; |
| 228 | } |
| 229 | |
| 230 | inst = kzalloc(sizeof(*inst), GFP_KERNEL); |
| 231 | if (!inst) { |
| 232 | pr_err("Unable to allocate video instance\n"); |
| 233 | goto err_no_mem; |
| 234 | } |
| 235 | mutex_init(&inst->sync_lock); |
| 236 | spin_lock_init(&inst->lock); |
| 237 | inst->session_type = session_type; |
| 238 | INIT_LIST_HEAD(&inst->pendingq); |
| 239 | INIT_LIST_HEAD(&inst->internalbufs); |
| 240 | INIT_LIST_HEAD(&inst->extradatabufs); |
| 241 | inst->state = MSM_VIDC_CORE_UNINIT_DONE; |
| 242 | inst->core = core; |
| 243 | for (i = SESSION_MSG_INDEX(SESSION_MSG_START); |
| 244 | i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) { |
| 245 | init_completion(&inst->completions[i]); |
| 246 | } |
| 247 | inst->mem_client = msm_smem_new_client(SMEM_ION); |
| 248 | if (!inst->mem_client) { |
| 249 | pr_err("Failed to create memory client\n"); |
| 250 | goto fail_mem_client; |
| 251 | } |
| 252 | if (session_type == MSM_VIDC_DECODER) { |
| 253 | msm_vdec_inst_init(inst); |
| 254 | msm_vdec_ctrl_init(inst); |
| 255 | } else if (session_type == MSM_VIDC_ENCODER) { |
| 256 | msm_venc_inst_init(inst); |
| 257 | } |
| 258 | rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, |
| 259 | session_type); |
| 260 | if (rc) { |
| 261 | pr_err("Failed to initialize vb2 queue on capture port\n"); |
| 262 | goto fail_init; |
| 263 | } |
| 264 | rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, |
| 265 | session_type); |
| 266 | if (rc) { |
| 267 | pr_err("Failed to initialize vb2 queue on capture port\n"); |
| 268 | goto fail_init; |
| 269 | } |
| 270 | rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT); |
| 271 | if (rc) { |
| 272 | pr_err("Failed to move video instance to init state\n"); |
| 273 | goto fail_init; |
| 274 | } |
| 275 | spin_lock_irqsave(&core->lock, flags); |
| 276 | list_add_tail(&inst->list, &core->instances); |
| 277 | spin_unlock_irqrestore(&core->lock, flags); |
| 278 | return inst; |
| 279 | fail_init: |
| 280 | msm_smem_delete_client(inst->mem_client); |
| 281 | fail_mem_client: |
| 282 | kfree(inst); |
| 283 | inst = NULL; |
| 284 | err_no_mem: |
| 285 | err_invalid_core: |
| 286 | return inst; |
| 287 | } |
| 288 | |
| 289 | static void cleanup_instance(struct msm_vidc_inst *inst) |
| 290 | { |
| 291 | unsigned long flags; |
| 292 | struct list_head *ptr, *next; |
| 293 | struct vb2_buf_entry *entry; |
| 294 | struct internal_buf *buf; |
| 295 | struct extradata_buf *ebuf; |
| 296 | if (inst) { |
| 297 | spin_lock_irqsave(&inst->lock, flags); |
| 298 | list_for_each_safe(ptr, next, &inst->pendingq) { |
| 299 | entry = list_entry(ptr, struct vb2_buf_entry, list); |
| 300 | list_del(&entry->list); |
| 301 | kfree(entry); |
| 302 | } |
| 303 | list_for_each_safe(ptr, next, &inst->internalbufs) { |
| 304 | buf = list_entry(ptr, struct internal_buf, list); |
| 305 | list_del(&buf->list); |
| 306 | msm_smem_free(inst->mem_client, buf->handle); |
| 307 | kfree(buf); |
| 308 | } |
| 309 | list_for_each_safe(ptr, next, &inst->extradatabufs) { |
| 310 | ebuf = list_entry(ptr, struct extradata_buf, list); |
| 311 | list_del(&ebuf->list); |
| 312 | msm_smem_free(inst->mem_client, ebuf->handle); |
| 313 | kfree(ebuf); |
| 314 | } |
| 315 | spin_unlock_irqrestore(&inst->lock, flags); |
| 316 | msm_smem_delete_client(inst->mem_client); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | int msm_vidc_close(void *instance) |
| 321 | { |
| 322 | struct msm_vidc_inst *inst = instance; |
| 323 | struct msm_vidc_inst *temp; |
| 324 | struct msm_vidc_core *core; |
| 325 | struct list_head *ptr, *next; |
| 326 | int rc = 0; |
| 327 | core = inst->core; |
| 328 | mutex_lock(&core->sync_lock); |
| 329 | list_for_each_safe(ptr, next, &core->instances) { |
| 330 | temp = list_entry(ptr, struct msm_vidc_inst, list); |
| 331 | if (temp == inst) { |
| 332 | list_del(&inst->list); |
| 333 | kfree(inst); |
| 334 | } |
| 335 | } |
| 336 | mutex_unlock(&core->sync_lock); |
| 337 | rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT); |
| 338 | if (rc) |
| 339 | pr_err("Failed to move video instance to init state\n"); |
| 340 | cleanup_instance(inst); |
| 341 | pr_debug("Closed the instance\n"); |
| 342 | return 0; |
| 343 | } |