diff options
Diffstat (limited to 'drivers/media/platform/s5p-fimc/fimc-lite.c')
-rw-r--r-- | drivers/media/platform/s5p-fimc/fimc-lite.c | 192 |
1 files changed, 133 insertions, 59 deletions
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 67db9f8102e4..bfc4206935c8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -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, }; @@ -1347,9 +1420,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; @@ -1516,7 +1590,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++) { |