]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/media/platform/s5p-fimc/fimc-lite.c
Merge branch 'stable' of git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux...
[karo-tx-linux.git] / drivers / media / platform / s5p-fimc / fimc-lite.c
index ed67220d0a64734bd56498c63555db5c732d4f5a..bbc35de7db278fe053c6de4d02be7fc7c40d4321 100644 (file)
@@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
        return def_fmt;
 }
 
-static int fimc_lite_hw_init(struct fimc_lite *fimc)
+static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output)
 {
        struct fimc_pipeline *pipeline = &fimc->pipeline;
-       struct fimc_sensor_info *sensor;
+       struct v4l2_subdev *sensor;
+       struct fimc_sensor_info *si;
        unsigned long flags;
 
-       if (pipeline->subdevs[IDX_SENSOR] == NULL)
+       sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR];
+
+       if (sensor == NULL)
                return -ENXIO;
 
        if (fimc->fmt == NULL)
                return -EINVAL;
 
-       sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
+       /* Get sensor configuration data from the sensor subdev */
+       si = v4l2_get_subdev_hostdata(sensor);
        spin_lock_irqsave(&fimc->slock, flags);
 
-       flite_hw_set_camera_bus(fimc, &sensor->pdata);
+       flite_hw_set_camera_bus(fimc, &si->pdata);
        flite_hw_set_source_format(fimc, &fimc->inp_frame);
        flite_hw_set_window_offset(fimc, &fimc->inp_frame);
-       flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
+       flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output);
        flite_hw_set_interrupt_mask(fimc);
        flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
 
@@ -256,7 +260,7 @@ static irqreturn_t flite_irq_handler(int irq, void *priv)
                wake_up(&fimc->irq_queue);
        }
 
-       if (fimc->out_path != FIMC_IO_DMA)
+       if (atomic_read(&fimc->out_path) != FIMC_IO_DMA)
                goto done;
 
        if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
@@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
 
        fimc->frame_count = 0;
 
-       ret = fimc_lite_hw_init(fimc);
+       ret = fimc_lite_hw_init(fimc, false);
        if (ret) {
                fimc_lite_reinit(fimc, false);
                return ret;
@@ -455,10 +459,16 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc)
 static int fimc_lite_open(struct file *file)
 {
        struct fimc_lite *fimc = video_drvdata(file);
+       struct media_entity *me = &fimc->vfd.entity;
        int ret;
 
-       if (mutex_lock_interruptible(&fimc->lock))
-               return -ERESTARTSYS;
+       mutex_lock(&me->parent->graph_mutex);
+
+       mutex_lock(&fimc->lock);
+       if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) {
+               ret = -EBUSY;
+               goto done;
+       }
 
        set_bit(ST_FLITE_IN_USE, &fimc->state);
        ret = pm_runtime_get_sync(&fimc->pdev->dev);
@@ -469,7 +479,8 @@ static int fimc_lite_open(struct file *file)
        if (ret < 0)
                goto done;
 
-       if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) {
+       if (++fimc->ref_count == 1 &&
+           atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
                ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
                                         &fimc->vfd.entity, true);
                if (ret < 0) {
@@ -483,6 +494,7 @@ static int fimc_lite_open(struct file *file)
        }
 done:
        mutex_unlock(&fimc->lock);
+       mutex_unlock(&me->parent->graph_mutex);
        return ret;
 }
 
@@ -493,7 +505,8 @@ static int fimc_lite_close(struct file *file)
 
        mutex_lock(&fimc->lock);
 
-       if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) {
+       if (--fimc->ref_count == 0 &&
+           atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
                clear_bit(ST_FLITE_IN_USE, &fimc->state);
                fimc_lite_stop_capture(fimc, false);
                fimc_pipeline_call(fimc, close, &fimc->pipeline);
@@ -598,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
        r->left = round_down(r->left, fimc->variant->win_hor_offs_align);
        r->top  = clamp_t(u32, r->top, 0, frame->f_height - r->height);
 
-       v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d",
+       v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n",
                 r->left, r->top, r->width, r->height,
                 frame->f_width, frame->f_height);
 }
@@ -618,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
        r->left = round_down(r->left, fimc->variant->out_hor_offs_align);
        r->top  = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
 
-       v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d",
+       v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n",
                 r->left, r->top, r->width, r->height,
                 frame->f_width, frame->f_height);
 }
@@ -962,6 +975,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
        .vidioc_streamoff               = fimc_lite_streamoff,
 };
 
+/* Called with the media graph mutex held */
+static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me)
+{
+       struct media_pad *pad = &me->pads[0];
+       struct v4l2_subdev *sd;
+
+       while (pad->flags & MEDIA_PAD_FL_SINK) {
+               /* source pad */
+               pad = media_entity_remote_source(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+
+               sd = media_entity_to_v4l2_subdev(pad->entity);
+
+               if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR)
+                       return sd;
+               /* sink pad */
+               pad = &sd->entity.pads[0];
+       }
+       return NULL;
+}
+
 /* Capture subdev media entity operations */
 static int fimc_lite_link_setup(struct media_entity *entity,
                                const struct media_pad *local,
@@ -970,46 +1006,60 @@ static int fimc_lite_link_setup(struct media_entity *entity,
        struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
        struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
        unsigned int remote_ent_type = media_entity_type(remote->entity);
+       int ret = 0;
 
        if (WARN_ON(fimc == NULL))
                return 0;
 
-       v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
-                __func__, local->entity->name, remote->entity->name,
+       v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n",
+                __func__, remote->entity->name, local->entity->name,
                 flags, fimc->source_subdev_grp_id);
 
-       switch (local->index) {
-       case FIMC_SD_PAD_SINK:
-               if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV)
-                       return -EINVAL;
+       mutex_lock(&fimc->lock);
 
+       switch (local->index) {
+       case FLITE_SD_PAD_SINK:
+               if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) {
+                       ret = -EINVAL;
+                       break;
+               }
                if (flags & MEDIA_LNK_FL_ENABLED) {
-                       if (fimc->source_subdev_grp_id != 0)
-                               return -EBUSY;
-                       fimc->source_subdev_grp_id = sd->grp_id;
-                       return 0;
+                       if (fimc->source_subdev_grp_id == 0)
+                               fimc->source_subdev_grp_id = sd->grp_id;
+                       else
+                               ret = -EBUSY;
+               } else {
+                       fimc->source_subdev_grp_id = 0;
+                       fimc->sensor = NULL;
                }
+               break;
 
-               fimc->source_subdev_grp_id = 0;
+       case FLITE_SD_PAD_SOURCE_DMA:
+               if (!(flags & MEDIA_LNK_FL_ENABLED))
+                       atomic_set(&fimc->out_path, FIMC_IO_NONE);
+               else if (remote_ent_type == MEDIA_ENT_T_DEVNODE)
+                       atomic_set(&fimc->out_path, FIMC_IO_DMA);
+               else
+                       ret = -EINVAL;
                break;
 
-       case FIMC_SD_PAD_SOURCE:
-               if (!(flags & MEDIA_LNK_FL_ENABLED)) {
-                       fimc->out_path = FIMC_IO_NONE;
-                       return 0;
-               }
-               if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
-                       fimc->out_path = FIMC_IO_ISP;
+       case FLITE_SD_PAD_SOURCE_ISP:
+               if (!(flags & MEDIA_LNK_FL_ENABLED))
+                       atomic_set(&fimc->out_path, FIMC_IO_NONE);
+               else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
+                       atomic_set(&fimc->out_path, FIMC_IO_ISP);
                else
-                       fimc->out_path = FIMC_IO_DMA;
+                       ret = -EINVAL;
                break;
 
        default:
                v4l2_err(sd, "Invalid pad index\n");
-               return -EINVAL;
+               ret = -EINVAL;
        }
+       mb();
 
-       return 0;
+       mutex_unlock(&fimc->lock);
+       return ret;
 }
 
 static const struct media_entity_operations fimc_lite_subdev_media_ops = {
@@ -1070,14 +1120,16 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
        struct flite_frame *source = &fimc->out_frame;
        const struct fimc_fmt *ffmt;
 
-       v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d",
+       v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n",
                 fmt->pad, mf->code, mf->width, mf->height);
 
        mf->colorspace = V4L2_COLORSPACE_JPEG;
        mutex_lock(&fimc->lock);
 
-       if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) ||
-           (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) {
+       if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP &&
+           sd->entity.stream_count > 0) ||
+           (atomic_read(&fimc->out_path) == FIMC_IO_DMA &&
+           vb2_is_busy(&fimc->vb_queue))) {
                mutex_unlock(&fimc->lock);
                return -EBUSY;
        }
@@ -1144,7 +1196,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
        }
        mutex_unlock(&fimc->lock);
 
-       v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+       v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
                 __func__, f->rect.left, f->rect.top, f->rect.width,
                 f->rect.height, f->f_width, f->f_height);
 
@@ -1178,7 +1230,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
        }
        mutex_unlock(&fimc->lock);
 
-       v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+       v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
                 __func__, f->rect.left, f->rect.top, f->rect.width,
                 f->rect.height, f->f_width, f->f_height);
 
@@ -1188,25 +1240,47 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
 static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
 {
        struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+       unsigned long flags;
+       int ret;
 
-       if (fimc->out_path == FIMC_IO_DMA)
-               return -ENOIOCTLCMD;
-
-       /* TODO: */
+       /*
+        * Find sensor subdev linked to FIMC-LITE directly or through
+        * MIPI-CSIS. This is required for configuration where FIMC-LITE
+        * is used as a subdev only and feeds data internally to FIMC-IS.
+        * The pipeline links are protected through entity.stream_count
+        * so there is no need to take the media graph mutex here.
+        */
+       fimc->sensor = __find_remote_sensor(&sd->entity);
 
-       return 0;
-}
+       if (atomic_read(&fimc->out_path) != FIMC_IO_ISP)
+               return -ENOIOCTLCMD;
 
-static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
-{
-       struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+       mutex_lock(&fimc->lock);
+       if (on) {
+               flite_hw_reset(fimc);
+               ret = fimc_lite_hw_init(fimc, true);
+               if (!ret) {
+                       spin_lock_irqsave(&fimc->slock, flags);
+                       flite_hw_capture_start(fimc);
+                       spin_unlock_irqrestore(&fimc->slock, flags);
+               }
+       } else {
+               set_bit(ST_FLITE_OFF, &fimc->state);
 
-       if (fimc->out_path == FIMC_IO_DMA)
-               return -ENOIOCTLCMD;
+               spin_lock_irqsave(&fimc->slock, flags);
+               flite_hw_capture_stop(fimc);
+               spin_unlock_irqrestore(&fimc->slock, flags);
 
-       /* TODO: */
+               ret = wait_event_timeout(fimc->irq_queue,
+                               !test_bit(ST_FLITE_OFF, &fimc->state),
+                               msecs_to_jiffies(200));
+               if (ret == 0)
+                       v4l2_err(sd, "s_stream(0) timeout\n");
+               clear_bit(ST_FLITE_RUN, &fimc->state);
+       }
 
-       return 0;
+       mutex_unlock(&fimc->lock);
+       return ret;
 }
 
 static int fimc_lite_log_status(struct v4l2_subdev *sd)
@@ -1227,7 +1301,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
        memset(vfd, 0, sizeof(*vfd));
 
        fimc->fmt = &fimc_lite_formats[0];
-       fimc->out_path = FIMC_IO_DMA;
+       atomic_set(&fimc->out_path, FIMC_IO_DMA);
 
        snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
                 fimc->index);
@@ -1308,7 +1382,6 @@ static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = {
 };
 
 static const struct v4l2_subdev_core_ops fimc_lite_core_ops = {
-       .s_power = fimc_lite_subdev_s_power,
        .log_status = fimc_lite_log_status,
 };
 
@@ -1335,6 +1408,7 @@ static const struct v4l2_ctrl_config fimc_lite_ctrl = {
        .id     = V4L2_CTRL_CLASS_USER | 0x1001,
        .type   = V4L2_CTRL_TYPE_BOOLEAN,
        .name   = "Test Pattern 640x480",
+       .step   = 1,
 };
 
 static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
@@ -1347,9 +1421,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
        sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
        snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);
 
-       fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
-       fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
-       ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+       fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+       fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE;
+       fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE;
+       ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM,
                                fimc->subdev_pads, 0);
        if (ret)
                return ret;
@@ -1426,11 +1501,9 @@ static int fimc_lite_probe(struct platform_device *pdev)
        mutex_init(&fimc->lock);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
-       if (fimc->regs == NULL) {
-               dev_err(&pdev->dev, "Failed to obtain io memory\n");
-               return -ENOENT;
-       }
+       fimc->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(fimc->regs))
+               return PTR_ERR(fimc->regs);
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (res == NULL) {
@@ -1518,7 +1591,7 @@ static int fimc_lite_resume(struct device *dev)
        INIT_LIST_HEAD(&fimc->active_buf_q);
        fimc_pipeline_call(fimc, open, &fimc->pipeline,
                           &fimc->vfd.entity, false);
-       fimc_lite_hw_init(fimc);
+       fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP);
        clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
 
        for (i = 0; i < fimc->reqbufs_count; i++) {