| /* linux/drivers/media/video/exynos/jpeg/jpeg_dev.c |
| * |
| * Copyright (c) 2012 Samsung Electronics Co., Ltd. |
| * http://www.samsung.com/ |
| * |
| * Core file for Samsung Jpeg v2.x Interface driver |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/version.h> |
| #include <linux/errno.h> |
| #include <linux/fs.h> |
| #include <linux/irq.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/slab.h> |
| #include <linux/err.h> |
| #include <linux/miscdevice.h> |
| #include <linux/platform_device.h> |
| #include <linux/mm.h> |
| #include <linux/init.h> |
| #include <linux/poll.h> |
| #include <linux/signal.h> |
| #include <linux/ioport.h> |
| #include <linux/kmod.h> |
| #include <linux/vmalloc.h> |
| #include <linux/time.h> |
| #include <linux/clk.h> |
| #include <linux/semaphore.h> |
| #include <linux/vmalloc.h> |
| #include <linux/workqueue.h> |
| |
| #include <asm/page.h> |
| |
| #include <mach/irqs.h> |
| |
| #ifdef CONFIG_PM_RUNTIME |
| #include <linux/pm_runtime.h> |
| #endif |
| |
| #include <media/v4l2-ioctl.h> |
| |
| #include "jpeg_core.h" |
| #include "jpeg_dev.h" |
| |
| #include "jpeg_mem.h" |
| #include "jpeg_regs.h" |
| #include "regs_jpeg_v2_x.h" |
| |
| static int jpeg_dec_queue_setup(struct vb2_queue *vq, |
| const struct v4l2_format *fmt, unsigned int *num_buffers, |
| unsigned int *num_planes, unsigned int sizes[], |
| void *allocators[]) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| |
| int i; |
| |
| if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
| *num_planes = ctx->param.dec_param.in_plane; |
| for (i = 0; i < ctx->param.dec_param.in_plane; i++) { |
| sizes[i] = ctx->param.dec_param.mem_size; |
| allocators[i] = ctx->dev->alloc_ctx; |
| } |
| } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { |
| *num_planes = ctx->param.dec_param.out_plane; |
| for (i = 0; i < ctx->param.dec_param.out_plane; i++) { |
| sizes[i] = (ctx->param.dec_param.out_width * |
| ctx->param.dec_param.out_height * |
| ctx->param.dec_param.out_depth[i]) / 8; |
| allocators[i] = ctx->dev->alloc_ctx; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int jpeg_dec_buf_prepare(struct vb2_buffer *vb) |
| { |
| int i; |
| int num_plane = 0; |
| |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
| |
| if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
| num_plane = ctx->param.dec_param.in_plane; |
| if (ctx->input_cacheable == 1) |
| ctx->dev->vb2->cache_flush(vb, num_plane); |
| } else if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { |
| num_plane = ctx->param.dec_param.out_plane; |
| if (ctx->output_cacheable == 1) |
| ctx->dev->vb2->cache_flush(vb, num_plane); |
| } |
| |
| for (i = 0; i < num_plane; i++) |
| vb2_set_plane_payload(vb, i, ctx->payload[i]); |
| |
| return 0; |
| } |
| |
| static void jpeg_dec_buf_queue(struct vb2_buffer *vb) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
| |
| if (ctx->m2m_ctx) |
| v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); |
| } |
| |
| static void jpeg_dec_lock(struct vb2_queue *vq) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| mutex_lock(&ctx->dev->lock); |
| } |
| |
| static void jpeg_dec_unlock(struct vb2_queue *vq) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| mutex_unlock(&ctx->dev->lock); |
| } |
| |
| static int jpeg_dec_stop_streaming(struct vb2_queue *q) |
| { |
| struct jpeg_ctx *ctx = q->drv_priv; |
| struct jpeg_dev *dev = ctx->dev; |
| |
| v4l2_m2m_get_next_job(dev->m2m_dev_dec, ctx->m2m_ctx); |
| |
| return 0; |
| } |
| |
| static int jpeg_enc_queue_setup(struct vb2_queue *vq, |
| const struct v4l2_format *fmt, unsigned int *num_buffers, |
| unsigned int *num_planes, unsigned int sizes[], |
| void *allocators[]) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| |
| int i; |
| if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
| *num_planes = ctx->param.enc_param.in_plane; |
| for (i = 0; i < ctx->param.enc_param.in_plane; i++) { |
| sizes[i] = (ctx->param.enc_param.in_width * |
| ctx->param.enc_param.in_height * |
| ctx->param.enc_param.in_depth[i]) / 8; |
| allocators[i] = ctx->dev->alloc_ctx; |
| } |
| |
| } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { |
| *num_planes = ctx->param.enc_param.out_plane; |
| for (i = 0; i < ctx->param.enc_param.in_plane; i++) { |
| sizes[i] = (ctx->param.enc_param.out_width * |
| ctx->param.enc_param.out_height * |
| ctx->param.enc_param.out_depth * 2) / 8; |
| allocators[i] = ctx->dev->alloc_ctx; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int jpeg_enc_buf_prepare(struct vb2_buffer *vb) |
| { |
| int i; |
| int num_plane = 0; |
| |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
| |
| if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { |
| num_plane = ctx->param.enc_param.in_plane; |
| if (ctx->input_cacheable == 1) |
| ctx->dev->vb2->cache_flush(vb, num_plane); |
| } else if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { |
| num_plane = ctx->param.enc_param.out_plane; |
| if (ctx->output_cacheable == 1) |
| ctx->dev->vb2->cache_flush(vb, num_plane); |
| } |
| |
| for (i = 0; i < num_plane; i++) |
| vb2_set_plane_payload(vb, i, ctx->payload[i]); |
| |
| return 0; |
| } |
| |
| static void jpeg_enc_buf_queue(struct vb2_buffer *vb) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); |
| |
| if (ctx->m2m_ctx) |
| v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); |
| } |
| |
| static void jpeg_enc_lock(struct vb2_queue *vq) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| mutex_lock(&ctx->dev->lock); |
| } |
| |
| static void jpeg_enc_unlock(struct vb2_queue *vq) |
| { |
| struct jpeg_ctx *ctx = vb2_get_drv_priv(vq); |
| mutex_unlock(&ctx->dev->lock); |
| } |
| |
| static int jpeg_enc_stop_streaming(struct vb2_queue *q) |
| { |
| struct jpeg_ctx *ctx = q->drv_priv; |
| struct jpeg_dev *dev = ctx->dev; |
| |
| v4l2_m2m_get_next_job(dev->m2m_dev_enc, ctx->m2m_ctx); |
| |
| return 0; |
| } |
| |
| static struct vb2_ops jpeg_enc_vb2_qops = { |
| .queue_setup = jpeg_enc_queue_setup, |
| .buf_prepare = jpeg_enc_buf_prepare, |
| .buf_queue = jpeg_enc_buf_queue, |
| .wait_prepare = jpeg_enc_lock, |
| .wait_finish = jpeg_enc_unlock, |
| .stop_streaming = jpeg_enc_stop_streaming, |
| }; |
| |
| static struct vb2_ops jpeg_dec_vb2_qops = { |
| .queue_setup = jpeg_dec_queue_setup, |
| .buf_prepare = jpeg_dec_buf_prepare, |
| .buf_queue = jpeg_dec_buf_queue, |
| .wait_prepare = jpeg_dec_lock, |
| .wait_finish = jpeg_dec_unlock, |
| .stop_streaming = jpeg_dec_stop_streaming, |
| }; |
| |
| static inline enum jpeg_node_type jpeg_get_node_type(struct file *file) |
| { |
| struct video_device *vdev = video_devdata(file); |
| |
| if (!vdev) { |
| jpeg_err("failed to get video_device\n"); |
| return JPEG_NODE_INVALID; |
| } |
| |
| jpeg_dbg("video_device index: %d\n", vdev->num); |
| |
| if (vdev->num == JPEG_NODE_DECODER) |
| return JPEG_NODE_DECODER; |
| else if (vdev->num == JPEG_NODE_ENCODER) |
| return JPEG_NODE_ENCODER; |
| else |
| return JPEG_NODE_INVALID; |
| } |
| |
| static int queue_init_dec(void *priv, struct vb2_queue *src_vq, |
| struct vb2_queue *dst_vq) |
| { |
| struct jpeg_ctx *ctx = priv; |
| int ret; |
| |
| memset(src_vq, 0, sizeof(*src_vq)); |
| src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
| src_vq->drv_priv = ctx; |
| src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
| src_vq->ops = &jpeg_dec_vb2_qops; |
| src_vq->mem_ops = ctx->dev->vb2->ops; |
| |
| ret = vb2_queue_init(src_vq); |
| if (ret) |
| return ret; |
| |
| memset(dst_vq, 0, sizeof(*dst_vq)); |
| dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
| dst_vq->drv_priv = ctx; |
| dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
| dst_vq->ops = &jpeg_dec_vb2_qops; |
| dst_vq->mem_ops = ctx->dev->vb2->ops; |
| |
| return vb2_queue_init(dst_vq); |
| } |
| |
| static int queue_init_enc(void *priv, struct vb2_queue *src_vq, |
| struct vb2_queue *dst_vq) |
| { |
| struct jpeg_ctx *ctx = priv; |
| int ret; |
| |
| memset(src_vq, 0, sizeof(*src_vq)); |
| src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
| src_vq->drv_priv = ctx; |
| src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
| src_vq->ops = &jpeg_enc_vb2_qops; |
| src_vq->mem_ops = ctx->dev->vb2->ops; |
| |
| ret = vb2_queue_init(src_vq); |
| if (ret) |
| return ret; |
| |
| memset(dst_vq, 0, sizeof(*dst_vq)); |
| dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; |
| dst_vq->drv_priv = ctx; |
| dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); |
| dst_vq->ops = &jpeg_enc_vb2_qops; |
| dst_vq->mem_ops = ctx->dev->vb2->ops; |
| |
| return vb2_queue_init(dst_vq); |
| } |
| static int jpeg_m2m_open(struct file *file) |
| { |
| struct jpeg_dev *dev = video_drvdata(file); |
| struct jpeg_ctx *ctx = NULL; |
| int ret = 0; |
| enum jpeg_node_type node; |
| |
| node = jpeg_get_node_type(file); |
| |
| if (node == JPEG_NODE_INVALID) { |
| jpeg_err("cannot specify node type\n"); |
| ret = -ENOENT; |
| goto err_node_type; |
| } |
| |
| ctx = kzalloc(sizeof *ctx, GFP_KERNEL); |
| if (!ctx) |
| return -ENOMEM; |
| |
| file->private_data = ctx; |
| ctx->dev = dev; |
| |
| spin_lock_init(&ctx->slock); |
| |
| if (node == JPEG_NODE_DECODER) |
| ctx->m2m_ctx = |
| v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, |
| queue_init_dec); |
| else |
| ctx->m2m_ctx = |
| v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, |
| queue_init_enc); |
| |
| if (IS_ERR(ctx->m2m_ctx)) { |
| int err = PTR_ERR(ctx->m2m_ctx); |
| kfree(ctx); |
| return err; |
| } |
| |
| clk_enable(dev->clk); |
| |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| dev->vb2->resume(dev->alloc_ctx); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_lock(dev->bus_dev, &dev->plat_dev->dev, BUSFREQ_400MHZ); |
| #endif |
| #else |
| pm_runtime_get_sync(&dev->plat_dev->dev); |
| #endif |
| #else |
| dev->vb2->resume(dev->alloc_ctx); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_lock(dev->bus_dev, &dev->plat_dev->dev, BUSFREQ_400MHZ); |
| #endif |
| #endif |
| |
| return 0; |
| |
| err_node_type: |
| kfree(ctx); |
| return ret; |
| } |
| |
| static int jpeg_m2m_release(struct file *file) |
| { |
| struct jpeg_ctx *ctx = file->private_data; |
| |
| v4l2_m2m_ctx_release(ctx->m2m_ctx); |
| |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| ctx->dev->vb2->suspend(ctx->dev->alloc_ctx); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* Unlock bus frequency */ |
| dev_unlock(ctx->dev->bus_dev, &ctx->dev->plat_dev->dev); |
| #endif |
| #else |
| pm_runtime_put_sync(&ctx->dev->plat_dev->dev); |
| #endif |
| #else |
| ctx->dev->vb2->suspend(ctx->dev->alloc_ctx); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* Unlock bus frequency */ |
| dev_unlock(ctx->dev->bus_dev, &ctx->dev->plat_dev->dev); |
| #endif |
| #endif |
| clk_disable(ctx->dev->clk); |
| kfree(ctx); |
| |
| return 0; |
| } |
| |
| static unsigned int jpeg_m2m_poll(struct file *file, |
| struct poll_table_struct *wait) |
| { |
| struct jpeg_ctx *ctx = file->private_data; |
| |
| return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); |
| } |
| |
| |
| static int jpeg_m2m_mmap(struct file *file, struct vm_area_struct *vma) |
| { |
| struct jpeg_ctx *ctx = file->private_data; |
| |
| return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); |
| } |
| |
| static const struct v4l2_file_operations jpeg_fops = { |
| .owner = THIS_MODULE, |
| .open = jpeg_m2m_open, |
| .release = jpeg_m2m_release, |
| .poll = jpeg_m2m_poll, |
| .unlocked_ioctl = video_ioctl2, |
| .mmap = jpeg_m2m_mmap, |
| }; |
| |
| static struct video_device jpeg_enc_videodev = { |
| .name = JPEG_ENC_NAME, |
| .fops = &jpeg_fops, |
| .minor = 12, |
| .release = video_device_release, |
| }; |
| |
| static struct video_device jpeg_dec_videodev = { |
| .name = JPEG_DEC_NAME, |
| .fops = &jpeg_fops, |
| .minor = 11, |
| .release = video_device_release, |
| }; |
| |
| static void jpeg_device_enc_run(void *priv) |
| { |
| struct jpeg_ctx *ctx = priv; |
| struct jpeg_dev *dev = ctx->dev; |
| struct jpeg_enc_param enc_param; |
| struct vb2_buffer *vb = NULL; |
| unsigned long flags; |
| |
| dev = ctx->dev; |
| spin_lock_irqsave(&ctx->slock, flags); |
| |
| dev->mode = ENCODING; |
| enc_param = ctx->param.enc_param; |
| |
| jpeg_sw_reset(dev->reg_base); |
| jpeg_set_interrupt(dev->reg_base); |
| jpeg_set_huf_table_enable(dev->reg_base, 1); |
| jpeg_set_enc_tbl(dev->reg_base, enc_param.quality); |
| jpeg_set_encode_tbl_select(dev->reg_base, enc_param.quality); |
| jpeg_set_stream_size(dev->reg_base, |
| enc_param.in_width, enc_param.in_height); |
| jpeg_set_enc_out_fmt(dev->reg_base, enc_param.out_fmt); |
| jpeg_set_enc_in_fmt(dev->reg_base, enc_param.in_fmt); |
| vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); |
| jpeg_set_stream_buf_address(dev->reg_base, dev->vb2->plane_addr(vb, 0)); |
| |
| vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); |
| jpeg_set_frame_buf_address(dev->reg_base, |
| enc_param.in_fmt, dev->vb2->plane_addr(vb, 0), enc_param.in_width, enc_param.in_height); |
| |
| jpeg_set_encode_hoff_cnt(dev->reg_base, enc_param.out_fmt); |
| |
| jpeg_set_timer_count(dev->reg_base, enc_param.in_width * enc_param.in_height * 32 + 0xff); |
| jpeg_set_enc_dec_mode(dev->reg_base, ENCODING); |
| |
| spin_unlock_irqrestore(&ctx->slock, flags); |
| } |
| |
| static void jpeg_device_dec_run(void *priv) |
| { |
| struct jpeg_ctx *ctx = priv; |
| struct jpeg_dev *dev = ctx->dev; |
| struct jpeg_dec_param dec_param; |
| struct vb2_buffer *vb = NULL; |
| unsigned long flags; |
| |
| dev = ctx->dev; |
| |
| spin_lock_irqsave(&ctx->slock, flags); |
| |
| dev->mode = DECODING; |
| dec_param = ctx->param.dec_param; |
| |
| jpeg_sw_reset(dev->reg_base); |
| jpeg_set_interrupt(dev->reg_base); |
| |
| jpeg_set_encode_tbl_select(dev->reg_base, 0); |
| |
| vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); |
| jpeg_set_stream_buf_address(dev->reg_base, dev->vb2->plane_addr(vb, 0)); |
| |
| vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); |
| jpeg_set_frame_buf_address(dev->reg_base, |
| dec_param.out_fmt, dev->vb2->plane_addr(vb, 0), dec_param.in_width, dec_param.in_height); |
| |
| if (dec_param.out_width > 0 && dec_param.out_height > 0) { |
| if ((dec_param.out_width * 2 == dec_param.in_width) && |
| (dec_param.out_height * 2 == dec_param.in_height)) |
| jpeg_set_dec_scaling(dev->reg_base, JPEG_SCALE_2, JPEG_SCALE_2); |
| else if ((dec_param.out_width * 4 == dec_param.in_width) && |
| (dec_param.out_height * 4 == dec_param.in_height)) |
| jpeg_set_dec_scaling(dev->reg_base, JPEG_SCALE_4, JPEG_SCALE_4); |
| else |
| jpeg_set_dec_scaling(dev->reg_base, JPEG_SCALE_NORMAL, JPEG_SCALE_NORMAL); |
| } |
| |
| jpeg_set_dec_out_fmt(dev->reg_base, dec_param.out_fmt); |
| jpeg_set_dec_bitstream_size(dev->reg_base, dec_param.size); |
| jpeg_set_timer_count(dev->reg_base, dec_param.in_width * dec_param.in_height * 8 + 0xff); |
| jpeg_set_enc_dec_mode(dev->reg_base, DECODING); |
| |
| spin_unlock_irqrestore(&ctx->slock, flags); |
| } |
| |
| static void jpeg_job_enc_abort(void *priv) |
| { |
| struct jpeg_ctx *ctx = priv; |
| struct jpeg_dev *dev = ctx->dev; |
| v4l2_m2m_get_next_job(dev->m2m_dev_enc, ctx->m2m_ctx); |
| } |
| |
| static void jpeg_job_dec_abort(void *priv) |
| { |
| struct jpeg_ctx *ctx = priv; |
| struct jpeg_dev *dev = ctx->dev; |
| v4l2_m2m_get_next_job(dev->m2m_dev_dec, ctx->m2m_ctx); |
| } |
| |
| static struct v4l2_m2m_ops jpeg_m2m_enc_ops = { |
| .device_run = jpeg_device_enc_run, |
| .job_abort = jpeg_job_enc_abort, |
| }; |
| |
| static struct v4l2_m2m_ops jpeg_m2m_dec_ops = { |
| .device_run = jpeg_device_dec_run, |
| .job_abort = jpeg_job_dec_abort, |
| }; |
| |
| int jpeg_int_pending(struct jpeg_dev *ctrl) |
| { |
| unsigned int int_status; |
| |
| int_status = jpeg_get_int_status(ctrl->reg_base); |
| jpeg_dbg("state(%d)\n", int_status); |
| |
| return int_status; |
| } |
| |
| static irqreturn_t jpeg_irq(int irq, void *priv) |
| { |
| unsigned int int_status; |
| struct vb2_buffer *src_vb, *dst_vb; |
| struct jpeg_dev *ctrl = priv; |
| struct jpeg_ctx *ctx; |
| unsigned long payload_size = 0; |
| |
| jpeg_clean_interrupt(ctrl->reg_base); |
| |
| if (ctrl->mode == ENCODING) |
| ctx = v4l2_m2m_get_curr_priv(ctrl->m2m_dev_enc); |
| else |
| ctx = v4l2_m2m_get_curr_priv(ctrl->m2m_dev_dec); |
| |
| if (ctx == 0) { |
| printk(KERN_ERR "ctx is null.\n"); |
| jpeg_sw_reset(ctrl->reg_base); |
| goto ctx_err; |
| } |
| |
| spin_lock(&ctx->slock); |
| |
| src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); |
| dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); |
| |
| int_status = jpeg_int_pending(ctrl); |
| |
| if (int_status) { |
| switch (int_status & 0x1ff) { |
| case 0x1: |
| ctrl->irq_ret = ERR_PROT; |
| break; |
| case 0x2: |
| ctrl->irq_ret = OK_ENC_OR_DEC; |
| break; |
| case 0x4: |
| ctrl->irq_ret = ERR_DEC_INVALID_FORMAT; |
| break; |
| case 0x8: |
| ctrl->irq_ret = ERR_MULTI_SCAN; |
| break; |
| case 0x10: |
| ctrl->irq_ret = ERR_FRAME; |
| break; |
| case 0x20: |
| ctrl->irq_ret = ERR_TIME_OUT; |
| break; |
| default: |
| ctrl->irq_ret = ERR_UNKNOWN; |
| break; |
| } |
| } else { |
| ctrl->irq_ret = ERR_UNKNOWN; |
| } |
| |
| if (ctrl->irq_ret == OK_ENC_OR_DEC) { |
| if (ctrl->mode == ENCODING) { |
| payload_size = jpeg_get_stream_size(ctrl->reg_base); |
| vb2_set_plane_payload(dst_vb, 0, payload_size); |
| } |
| v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); |
| v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); |
| } else { |
| v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); |
| v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); |
| } |
| |
| if (ctrl->mode == ENCODING) |
| v4l2_m2m_job_finish(ctrl->m2m_dev_enc, ctx->m2m_ctx); |
| else |
| v4l2_m2m_job_finish(ctrl->m2m_dev_dec, ctx->m2m_ctx); |
| |
| spin_unlock(&ctx->slock); |
| ctx_err: |
| return IRQ_HANDLED; |
| } |
| |
| static int jpeg_setup_controller(struct jpeg_dev *ctrl) |
| { |
| mutex_init(&ctrl->lock); |
| init_waitqueue_head(&ctrl->wq); |
| |
| return 0; |
| } |
| |
| static int jpeg_probe(struct platform_device *pdev) |
| { |
| struct jpeg_dev *dev; |
| struct video_device *vfd; |
| struct resource *res; |
| int ret; |
| |
| /* global structure */ |
| dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
| if (!dev) { |
| dev_err(&pdev->dev, "%s: not enough memory\n", |
| __func__); |
| ret = -ENOMEM; |
| goto err_alloc; |
| } |
| |
| dev->plat_dev = pdev; |
| |
| /* setup jpeg control */ |
| ret = jpeg_setup_controller(dev); |
| if (ret) { |
| jpeg_err("failed to setup controller\n"); |
| goto err_setup; |
| } |
| |
| /* memory region */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| jpeg_err("failed to get jpeg memory region resource\n"); |
| ret = -ENOENT; |
| goto err_res; |
| } |
| |
| res = request_mem_region(res->start, resource_size(res), |
| pdev->name); |
| if (!res) { |
| jpeg_err("failed to request jpeg io memory region\n"); |
| ret = -ENOMEM; |
| goto err_region; |
| } |
| |
| /* ioremap */ |
| dev->reg_base = ioremap(res->start, resource_size(res)); |
| if (!dev->reg_base) { |
| jpeg_err("failed to remap jpeg io region\n"); |
| ret = -ENOENT; |
| goto err_map; |
| } |
| |
| /* irq */ |
| res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| if (!res) { |
| jpeg_err("failed to request jpeg irq resource\n"); |
| ret = -ENOENT; |
| goto err_irq; |
| } |
| |
| dev->irq_no = res->start; |
| ret = request_irq(dev->irq_no, (void *)jpeg_irq, |
| IRQF_DISABLED, pdev->name, dev); |
| if (ret != 0) { |
| jpeg_err("failed to jpeg request irq\n"); |
| ret = -ENOENT; |
| goto err_irq; |
| } |
| |
| /* clock */ |
| dev->clk = clk_get(&pdev->dev, "jpeg"); |
| if (IS_ERR(dev->clk)) { |
| jpeg_err("failed to find jpeg clock source\n"); |
| ret = -ENOENT; |
| goto err_clk; |
| } |
| |
| #ifdef CONFIG_PM_RUNTIME |
| #ifndef CONFIG_CPU_EXYNOS5250 |
| pm_runtime_enable(&pdev->dev); |
| #endif |
| #endif |
| |
| /* clock enable */ |
| clk_enable(dev->clk); |
| |
| ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); |
| if (ret) { |
| v4l2_err(&dev->v4l2_dev, "Failed to register v4l2 device\n"); |
| goto err_v4l2; |
| } |
| |
| /* encoder */ |
| vfd = video_device_alloc(); |
| if (!vfd) { |
| v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); |
| ret = -ENOMEM; |
| goto err_vd_alloc_enc; |
| } |
| |
| *vfd = jpeg_enc_videodev; |
| vfd->ioctl_ops = get_jpeg_enc_v4l2_ioctl_ops(); |
| ret = video_register_device(vfd, VFL_TYPE_GRABBER, 12); |
| if (ret) { |
| v4l2_err(&dev->v4l2_dev, |
| "%s(): failed to register video device\n", __func__); |
| video_device_release(vfd); |
| goto err_vd_alloc_enc; |
| } |
| v4l2_info(&dev->v4l2_dev, |
| "JPEG driver is registered to /dev/video%d\n", vfd->num); |
| |
| dev->vfd_enc = vfd; |
| dev->m2m_dev_enc = v4l2_m2m_init(&jpeg_m2m_enc_ops); |
| if (IS_ERR(dev->m2m_dev_enc)) { |
| v4l2_err(&dev->v4l2_dev, |
| "failed to initialize v4l2-m2m device\n"); |
| ret = PTR_ERR(dev->m2m_dev_enc); |
| goto err_m2m_init_enc; |
| } |
| video_set_drvdata(vfd, dev); |
| |
| /* decoder */ |
| vfd = video_device_alloc(); |
| if (!vfd) { |
| v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); |
| ret = -ENOMEM; |
| goto err_vd_alloc_dec; |
| } |
| |
| *vfd = jpeg_dec_videodev; |
| vfd->ioctl_ops = get_jpeg_dec_v4l2_ioctl_ops(); |
| ret = video_register_device(vfd, VFL_TYPE_GRABBER, 11); |
| if (ret) { |
| v4l2_err(&dev->v4l2_dev, |
| "%s(): failed to register video device\n", __func__); |
| video_device_release(vfd); |
| goto err_vd_alloc_dec; |
| } |
| v4l2_info(&dev->v4l2_dev, |
| "JPEG driver is registered to /dev/video%d\n", vfd->num); |
| |
| dev->vfd_dec = vfd; |
| dev->m2m_dev_dec = v4l2_m2m_init(&jpeg_m2m_dec_ops); |
| if (IS_ERR(dev->m2m_dev_dec)) { |
| v4l2_err(&dev->v4l2_dev, |
| "failed to initialize v4l2-m2m device\n"); |
| ret = PTR_ERR(dev->m2m_dev_dec); |
| goto err_m2m_init_dec; |
| } |
| video_set_drvdata(vfd, dev); |
| |
| platform_set_drvdata(pdev, dev); |
| |
| #ifdef CONFIG_VIDEOBUF2_CMA_PHYS |
| dev->vb2 = &jpeg_vb2_cma; |
| #elif defined(CONFIG_VIDEOBUF2_ION) |
| dev->vb2 = &jpeg_vb2_ion; |
| #endif |
| dev->alloc_ctx = dev->vb2->init(dev); |
| |
| if (IS_ERR(dev->alloc_ctx)) { |
| ret = PTR_ERR(dev->alloc_ctx); |
| goto err_video_reg; |
| } |
| |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* To lock bus frequency in OPP mode */ |
| dev->bus_dev = dev_get("exynos-busfreq"); |
| #endif |
| |
| /* clock disable */ |
| clk_disable(dev->clk); |
| |
| return 0; |
| |
| err_video_reg: |
| v4l2_m2m_release(dev->m2m_dev_dec); |
| err_m2m_init_dec: |
| video_unregister_device(dev->vfd_dec); |
| video_device_release(dev->vfd_dec); |
| err_vd_alloc_dec: |
| v4l2_m2m_release(dev->m2m_dev_enc); |
| err_m2m_init_enc: |
| video_unregister_device(dev->vfd_enc); |
| video_device_release(dev->vfd_enc); |
| err_vd_alloc_enc: |
| v4l2_device_unregister(&dev->v4l2_dev); |
| err_v4l2: |
| clk_disable(dev->clk); |
| clk_put(dev->clk); |
| err_clk: |
| free_irq(dev->irq_no, NULL); |
| err_irq: |
| iounmap(dev->reg_base); |
| err_map: |
| err_region: |
| kfree(res); |
| err_res: |
| mutex_destroy(&dev->lock); |
| err_setup: |
| kfree(dev); |
| err_alloc: |
| return ret; |
| |
| } |
| |
| static int jpeg_remove(struct platform_device *pdev) |
| { |
| struct jpeg_dev *dev = platform_get_drvdata(pdev); |
| |
| v4l2_m2m_release(dev->m2m_dev_enc); |
| video_unregister_device(dev->vfd_enc); |
| |
| v4l2_m2m_release(dev->m2m_dev_dec); |
| video_unregister_device(dev->vfd_dec); |
| |
| v4l2_device_unregister(&dev->v4l2_dev); |
| |
| dev->vb2->cleanup(dev->alloc_ctx); |
| |
| free_irq(dev->irq_no, pdev); |
| mutex_destroy(&dev->lock); |
| iounmap(dev->reg_base); |
| |
| clk_put(dev->clk); |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_unlock(dev->bus_dev, &pdev->dev); |
| #endif |
| #else |
| pm_runtime_put_sync(&pdev->dev); |
| pm_runtime_disable(&pdev->dev); |
| #endif |
| #else |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_unlock(dev->bus_dev, &pdev->dev); |
| #endif |
| #endif |
| kfree(dev); |
| return 0; |
| } |
| |
| static int jpeg_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| struct jpeg_dev *dev = platform_get_drvdata(pdev); |
| |
| if (dev->ctx) { |
| dev->vb2->suspend(dev->alloc_ctx); |
| clk_disable(dev->clk); |
| } |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_unlock(dev->bus_dev, &pdev->dev); |
| #endif |
| #else |
| pm_runtime_put_sync(&pdev->dev); |
| #endif |
| #else |
| struct jpeg_dev *dev = platform_get_drvdata(pdev); |
| |
| if (dev->ctx) { |
| dev->vb2->suspend(dev->alloc_ctx); |
| clk_disable(dev->clk); |
| } |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_unlock(dev->bus_dev, &pdev->dev); |
| #endif |
| #endif |
| return 0; |
| } |
| |
| static int jpeg_resume(struct platform_device *pdev) |
| { |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| struct jpeg_dev *dev = platform_get_drvdata(pdev); |
| |
| if (dev->ctx) { |
| clk_enable(dev->clk); |
| dev->vb2->resume(dev->alloc_ctx); |
| } |
| #else |
| pm_runtime_get_sync(&pdev->dev); |
| #endif |
| #else |
| struct jpeg_dev *dev = platform_get_drvdata(pdev); |
| |
| if (dev->ctx) { |
| clk_enable(dev->clk); |
| dev->vb2->resume(dev->alloc_ctx); |
| } |
| #endif |
| return 0; |
| } |
| |
| int jpeg_suspend_pd(struct device *dev) |
| { |
| struct platform_device *pdev; |
| int ret; |
| pm_message_t state; |
| |
| state.event = 0; |
| pdev = to_platform_device(dev); |
| ret = jpeg_suspend(pdev, state); |
| |
| return 0; |
| } |
| |
| int jpeg_resume_pd(struct device *dev) |
| { |
| struct platform_device *pdev; |
| int ret; |
| |
| pdev = to_platform_device(dev); |
| ret = jpeg_resume(pdev); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM_RUNTIME |
| static int jpeg_runtime_suspend(struct device *dev) |
| { |
| struct platform_device *pdev = to_platform_device(dev); |
| struct jpeg_dev *jpeg_drv = platform_get_drvdata(pdev); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_unlock(jpeg_drv->bus_dev, dev); |
| #endif |
| jpeg_drv->vb2->suspend(jpeg_drv->alloc_ctx); |
| /* clock disable */ |
| clk_disable(jpeg_drv->clk); |
| return 0; |
| } |
| |
| static int jpeg_runtime_resume(struct device *dev) |
| { |
| struct platform_device *pdev = to_platform_device(dev); |
| struct jpeg_dev *jpeg_drv = platform_get_drvdata(pdev); |
| #ifdef CONFIG_BUSFREQ_OPP |
| /* lock bus frequency */ |
| dev_lock(jpeg_drv->bus_dev, &jpeg_drv->plat_dev->dev, BUSFREQ_400MHZ); |
| #endif |
| clk_enable(jpeg_drv->clk); |
| jpeg_drv->vb2->resume(jpeg_drv->alloc_ctx); |
| return 0; |
| } |
| #endif |
| |
| static const struct dev_pm_ops jpeg_pm_ops = { |
| .suspend = jpeg_suspend_pd, |
| .resume = jpeg_resume_pd, |
| #ifdef CONFIG_PM_RUNTIME |
| .runtime_suspend = jpeg_runtime_suspend, |
| .runtime_resume = jpeg_runtime_resume, |
| #endif |
| }; |
| static struct platform_driver jpeg_driver = { |
| .probe = jpeg_probe, |
| .remove = jpeg_remove, |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| .suspend = jpeg_suspend, |
| .resume = jpeg_resume, |
| #else |
| #ifndef CONFIG_PM_RUNTIME |
| .suspend = jpeg_suspend, |
| .resume = jpeg_resume, |
| #endif |
| #endif |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = JPEG_NAME, |
| #ifdef CONFIG_PM_RUNTIME |
| #if defined (CONFIG_CPU_EXYNOS5250) |
| .pm = NULL, |
| #else |
| .pm = &jpeg_pm_ops, |
| #endif |
| #else |
| .pm = NULL, |
| #endif |
| }, |
| }; |
| |
| static int __init jpeg_init(void) |
| { |
| printk(KERN_CRIT "Initialize JPEG driver\n"); |
| |
| platform_driver_register(&jpeg_driver); |
| |
| return 0; |
| } |
| |
| static void __exit jpeg_exit(void) |
| { |
| platform_driver_unregister(&jpeg_driver); |
| } |
| |
| module_init(jpeg_init); |
| module_exit(jpeg_exit); |
| |
| MODULE_AUTHOR("ym.song@samsung.com>"); |
| MODULE_DESCRIPTION("JPEG v2.x H/W Device Driver"); |
| MODULE_LICENSE("GPL"); |