diff options
Diffstat (limited to 'drivers/media/platform')
48 files changed, 4742 insertions, 1781 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8b89ebe16d94..201f5c296a95 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -54,15 +54,6 @@ config VIDEO_VIU Say Y here if you want to enable VIU device on MPC5121e Rev2+. In doubt, say N. -config VIDEO_TIMBERDALE - tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST - select VIDEO_ADV7180 - select VIDEOBUF_DMA_CONTIG - ---help--- - Add support for the Video In peripherial of the timberdale FPGA. - config VIDEO_M32R_AR tristate "AR devices" depends on VIDEO_V4L2 @@ -120,6 +111,19 @@ source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" +config VIDEO_TI_CAL + tristate "TI CAL (Camera Adaptation Layer) driver" + depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on SOC_DRA7XX || COMPILE_TEST + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + default n + ---help--- + Support for the TI CAL (Camera Adaptation Layer) block + found on DRA72X SoC. + In TI Technical Reference Manual this module is referred as + Camera Interface Subsystem (CAMSS). + endif # V4L_PLATFORM_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index efa0295af87b..bbb7bd1eb268 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -2,7 +2,6 @@ # Makefile for the video capture/playback device drivers. # -obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o @@ -18,6 +17,8 @@ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/ +obj-$(CONFIG_VIDEO_TI_CAL) += ti-vpe/ + obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda/ diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 7d28899f89ce..6efe9d002961 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1342,7 +1342,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) /* Calculate bytesused field */ if (dst_buf->sequence == 0) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, wr_ptr - start_ptr + ctx->vpu_header_size[0] + ctx->vpu_header_size[1] + ctx->vpu_header_size[2]); diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index ffbefdff6b5e..6fba32bec974 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -261,7 +261,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) */ if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { if (fpc_physaddr != NULL) { - free_pages((unsigned long)fpc_physaddr, + free_pages((unsigned long)fpc_virtaddr, get_order (config_params->fault_pxl.fp_num * FP_NUM_BYTES)); diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 93782f15b825..a600e32e2543 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -700,7 +700,7 @@ static unsigned int gsc_m2m_poll(struct file *file, { struct gsc_ctx *ctx = fh_to_ctx(file->private_data); struct gsc_dev *gsc = ctx->gsc_dev; - int ret; + unsigned int ret; if (mutex_lock_interruptible(&gsc->lock)) return -ERESTARTSYS; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index e79ddbb1e14f..feb521f28e14 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -389,13 +389,19 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, struct fimc_source_info *pd = &fmd->sensor[index].pdata; struct device_node *rem, *ep, *np; struct v4l2_of_endpoint endpoint; + int ret; /* Assume here a port node can have only one endpoint node. */ ep = of_get_next_child(port, NULL); if (!ep) return 0; - v4l2_of_parse_endpoint(ep, &endpoint); + ret = v4l2_of_parse_endpoint(ep, &endpoint); + if (ret) { + of_node_put(ep); + return ret; + } + if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS) return -EINVAL; @@ -486,8 +492,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) continue; ret = fimc_md_parse_port_node(fmd, port, index); - if (ret < 0) + if (ret < 0) { + of_node_put(node); goto rpm_put; + } index++; } @@ -498,8 +506,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) for_each_child_of_node(ports, node) { ret = fimc_md_parse_port_node(fmd, node, index); - if (ret < 0) + if (ret < 0) { + of_node_put(node); break; + } index++; } rpm_put: @@ -707,8 +717,10 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd, ret = fimc_md_register_platform_entity(fmd, pdev, plat_entity); put_device(&pdev->dev); - if (ret < 0) + if (ret < 0) { + of_node_put(node); break; + } } return ret; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index ac5e50e595be..bd5c46c3d4b7 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -736,6 +736,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, { struct device_node *node = pdev->dev.of_node; struct v4l2_of_endpoint endpoint; + int ret; if (of_property_read_u32(node, "clock-frequency", &state->clk_frequency)) @@ -751,7 +752,9 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, return -EINVAL; } /* Get port node and validate MIPI-CSI channel id. */ - v4l2_of_parse_endpoint(node, &endpoint); + ret = v4l2_of_parse_endpoint(node, &endpoint); + if (ret) + goto err; state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0; if (state->index >= CSIS_MAX_ENTITIES) @@ -764,9 +767,10 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, "samsung,csis-wclk"); state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; - of_node_put(node); - return 0; +err: + of_node_put(node); + return ret; } static int s5pcsis_pm_resume(struct device *dev, bool runtime); diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 0bcfa553c1aa..f9e5245f26ac 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -449,7 +449,7 @@ void omap3isp_configure_bridge(struct isp_device *isp, case CCDC_INPUT_PARALLEL: ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; - shift += parcfg->data_lane_shift * 2; + shift += parcfg->data_lane_shift; break; case CCDC_INPUT_CSI2A: @@ -2235,8 +2235,11 @@ static int isp_of_parse_node(struct device *dev, struct device_node *node, struct isp_bus_cfg *buscfg = &isd->bus; struct v4l2_of_endpoint vep; unsigned int i; + int ret; - v4l2_of_parse_endpoint(node, &vep); + ret = v4l2_of_parse_endpoint(node, &vep); + if (ret) + return ret; dev_dbg(dev, "parsing endpoint %s, interface %u\n", node->full_name, vep.base.port); @@ -2528,12 +2531,13 @@ static int isp_probe(struct platform_device *pdev) } /* Interrupt */ - isp->irq_num = platform_get_irq(pdev, 0); - if (isp->irq_num <= 0) { + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { dev_err(isp->dev, "No IRQ resource\n"); ret = -ENODEV; goto error_iommu; } + isp->irq_num = ret; if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { @@ -2599,6 +2603,7 @@ static const struct of_device_id omap3isp_of_table[] = { { .compatible = "ti,omap3-isp" }, { }, }; +MODULE_DEVICE_TABLE(of, omap3isp_of_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index bb3974c98e37..882310eb45cc 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -2421,7 +2421,7 @@ static int ccdc_link_validate(struct v4l2_subdev *sd, &((struct isp_bus_cfg *) media_entity_to_v4l2_subdev(link->source->entity) ->host_priv)->bus.parallel; - parallel_shift = parcfg->data_lane_shift * 2; + parallel_shift = parcfg->data_lane_shift; } else { parallel_shift = 0; } diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index 84a96670e2e7..ac30a0f83780 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -1480,13 +1480,6 @@ static void preview_isr_buffer(struct isp_prev_device *prev) struct isp_buffer *buffer; int restart = 0; - if (prev->input == PREVIEW_INPUT_MEMORY) { - buffer = omap3isp_video_buffer_next(&prev->video_in); - if (buffer != NULL) - preview_set_inaddr(prev, buffer->dma); - pipe->state |= ISP_PIPELINE_IDLE_INPUT; - } - if (prev->output & PREVIEW_OUTPUT_MEMORY) { buffer = omap3isp_video_buffer_next(&prev->video_out); if (buffer != NULL) { @@ -1496,6 +1489,13 @@ static void preview_isr_buffer(struct isp_prev_device *prev) pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; } + if (prev->input == PREVIEW_INPUT_MEMORY) { + buffer = omap3isp_video_buffer_next(&prev->video_in); + if (buffer != NULL) + preview_set_inaddr(prev, buffer->dma); + pipe->state |= ISP_PIPELINE_IDLE_INPUT; + } + switch (prev->state) { case ISP_PIPELINE_STREAM_SINGLESHOT: if (isp_pipeline_ready(pipe)) diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 994dfc0813f6..2aff755ff77c 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -434,10 +434,68 @@ static void isp_video_buffer_queue(struct vb2_buffer *buf) } } +/* + * omap3isp_video_return_buffers - Return all queued buffers to videobuf2 + * @video: ISP video object + * @state: new state for the returned buffers + * + * Return all buffers queued on the video node to videobuf2 in the given state. + * The buffer state should be VB2_BUF_STATE_QUEUED if called due to an error + * when starting the stream, or VB2_BUF_STATE_ERROR otherwise. + * + * The function must be called with the video irqlock held. + */ +static void omap3isp_video_return_buffers(struct isp_video *video, + enum vb2_buffer_state state) +{ + while (!list_empty(&video->dmaqueue)) { + struct isp_buffer *buf; + + buf = list_first_entry(&video->dmaqueue, + struct isp_buffer, irqlist); + list_del(&buf->irqlist); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } +} + +static int isp_video_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct isp_video_fh *vfh = vb2_get_drv_priv(queue); + struct isp_video *video = vfh->video; + struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); + unsigned long flags; + int ret; + + /* In sensor-to-memory mode, the stream can be started synchronously + * to the stream on command. In memory-to-memory mode, it will be + * started when buffers are queued on both the input and output. + */ + if (pipe->input) + return 0; + + ret = omap3isp_pipeline_set_stream(pipe, + ISP_PIPELINE_STREAM_CONTINUOUS); + if (ret < 0) { + spin_lock_irqsave(&video->irqlock, flags); + omap3isp_video_return_buffers(video, VB2_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&video->irqlock, flags); + return ret; + } + + spin_lock_irqsave(&video->irqlock, flags); + if (list_empty(&video->dmaqueue)) + video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; + spin_unlock_irqrestore(&video->irqlock, flags); + + return 0; +} + static const struct vb2_ops isp_video_queue_ops = { .queue_setup = isp_video_queue_setup, .buf_prepare = isp_video_buffer_prepare, .buf_queue = isp_video_buffer_queue, + .start_streaming = isp_video_start_streaming, }; /* @@ -459,7 +517,7 @@ static const struct vb2_ops isp_video_queue_ops = { struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) { struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); - enum isp_pipeline_state state; + enum vb2_buffer_state vb_state; struct isp_buffer *buf; unsigned long flags; @@ -495,17 +553,19 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) /* Report pipeline errors to userspace on the capture device side. */ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { - state = VB2_BUF_STATE_ERROR; + vb_state = VB2_BUF_STATE_ERROR; pipe->error = false; } else { - state = VB2_BUF_STATE_DONE; + vb_state = VB2_BUF_STATE_DONE; } - vb2_buffer_done(&buf->vb.vb2_buf, state); + vb2_buffer_done(&buf->vb.vb2_buf, vb_state); spin_lock_irqsave(&video->irqlock, flags); if (list_empty(&video->dmaqueue)) { + enum isp_pipeline_state state; + spin_unlock_irqrestore(&video->irqlock, flags); if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -541,26 +601,16 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video) * omap3isp_video_cancel_stream - Cancel stream on a video node * @video: ISP video object * - * Cancelling a stream mark all buffers on the video node as erroneous and makes - * sure no new buffer can be queued. + * Cancelling a stream returns all buffers queued on the video node to videobuf2 + * in the erroneous state and makes sure no new buffer can be queued. */ void omap3isp_video_cancel_stream(struct isp_video *video) { unsigned long flags; spin_lock_irqsave(&video->irqlock, flags); - - while (!list_empty(&video->dmaqueue)) { - struct isp_buffer *buf; - - buf = list_first_entry(&video->dmaqueue, - struct isp_buffer, irqlist); - list_del(&buf->irqlist); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - + omap3isp_video_return_buffers(video, VB2_BUF_STATE_ERROR); video->error = true; - spin_unlock_irqrestore(&video->irqlock, flags); } @@ -1087,29 +1137,10 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_check_format; - /* In sensor-to-memory mode, the stream can be started synchronously - * to the stream on command. In memory-to-memory mode, it will be - * started when buffers are queued on both the input and output. - */ - if (pipe->input == NULL) { - ret = omap3isp_pipeline_set_stream(pipe, - ISP_PIPELINE_STREAM_CONTINUOUS); - if (ret < 0) - goto err_set_stream; - spin_lock_irqsave(&video->irqlock, flags); - if (list_empty(&video->dmaqueue)) - video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; - spin_unlock_irqrestore(&video->irqlock, flags); - } - mutex_unlock(&video->stream_lock); return 0; -err_set_stream: - mutex_lock(&video->queue_lock); - vb2_streamoff(&vfh->queue, type); - mutex_unlock(&video->queue_lock); err_check_format: media_entity_pipeline_stop(&video->video.entity); err_pipeline_start: diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/omap3isp/omap3isp.h index 190e259a6a2d..443e8f7673e2 100644 --- a/drivers/media/platform/omap3isp/omap3isp.h +++ b/drivers/media/platform/omap3isp/omap3isp.h @@ -33,9 +33,9 @@ enum isp_interface_type { * struct isp_parallel_cfg - Parallel interface configuration * @data_lane_shift: Data lane shifter * 0 - CAMEXT[13:0] -> CAM[13:0] - * 1 - CAMEXT[13:2] -> CAM[11:0] - * 2 - CAMEXT[13:4] -> CAM[9:0] - * 3 - CAMEXT[13:6] -> CAM[7:0] + * 2 - CAMEXT[13:2] -> CAM[11:0] + * 4 - CAMEXT[13:4] -> CAM[9:0] + * 6 - CAMEXT[13:6] -> CAM[7:0] * @clk_pol: Pixel clock polarity * 0 - Sample on rising edge, 1 - Sample on falling edge * @hs_pol: Horizontal synchronization polarity @@ -48,7 +48,7 @@ enum isp_interface_type { * 0 - Normal, 1 - One's complement */ struct isp_parallel_cfg { - unsigned int data_lane_shift:2; + unsigned int data_lane_shift:3; unsigned int clk_pol:1; unsigned int hs_pol:1; unsigned int vs_pol:1; diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 485f5259acb0..552789a69c86 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1613,6 +1613,7 @@ static const struct of_device_id jpu_dt_ids[] = { { .compatible = "renesas,jpu-r8a7791" }, /* M2-W */ { .compatible = "renesas,jpu-r8a7792" }, /* V2H */ { .compatible = "renesas,jpu-r8a7793" }, /* M2-N */ + { .compatible = "renesas,rcar-gen2-jpu" }, { }, }; MODULE_DEVICE_TABLE(of, jpu_dt_ids); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 0434f02a7175..034b5c1d35a1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -212,6 +212,14 @@ static struct mfc_control controls[] = { .menu_skip_mask = 0, }, { + .id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE, .type = V4L2_CTRL_TYPE_INTEGER, .minimum = 0, @@ -1423,6 +1431,10 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE: ctx->force_frame_type = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + ctx->force_frame_type = + V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME; + break; case V4L2_CID_MPEG_VIDEO_VBV_SIZE: p->vbv_size = ctrl->val; break; diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index b7fd695b9ed5..dc75a80794fb 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -143,6 +143,7 @@ #define RCAR_VIN_BT656 (1 << 3) enum chip_id { + RCAR_GEN3, RCAR_GEN2, RCAR_H1, RCAR_M1, @@ -1818,6 +1819,7 @@ static struct soc_camera_host_ops rcar_vin_host_ops = { #ifdef CONFIG_OF static const struct of_device_id rcar_vin_of_table[] = { + { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_GEN3 }, { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 90c87f2b4ec0..b9f369c0fb94 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -213,8 +213,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { - struct soc_camera_device *icd = container_of(vq, - struct soc_camera_device, vb2_vidq); + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -361,8 +360,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb) static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = container_of(vb->vb2_queue, - struct soc_camera_device, vb2_vidq); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); @@ -413,8 +411,7 @@ error: static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = container_of(vb->vb2_queue, - struct soc_camera_device, vb2_vidq); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vbuf); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -444,8 +441,7 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = container_of(vb->vb2_queue, - struct soc_camera_device, vb2_vidq); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -460,7 +456,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) { - struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); + struct soc_camera_device *icd = soc_camera_from_vb2q(q); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct list_head *buf_head, *tmp; diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c index 69d7fe4471c2..2c0015b1264d 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c @@ -118,7 +118,7 @@ int c8sectpfe_frontend_attach(struct dvb_frontend **fe, struct channel_info *tsin, int chan_num) { struct tda18212_config *tda18212; - struct stv6110x_devctl *fe2; + const struct stv6110x_devctl *fe2; struct i2c_client *client; struct i2c_board_info tda18212_info = { .type = "tda18212", diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile index be680f839e77..e236059a60ad 100644 --- a/drivers/media/platform/ti-vpe/Makefile +++ b/drivers/media/platform/ti-vpe/Makefile @@ -3,3 +3,7 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o ti-vpe-y := vpe.o sc.o csc.o vpdma.o ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG + +obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o + +ti-cal-y := cal.o diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c new file mode 100644 index 000000000000..35fa1071c5b2 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal.c @@ -0,0 +1,1971 @@ +/* + * TI CAL camera interface driver + * + * Copyright (c) 2015 Texas Instruments Inc. + * Benoit Parrot, <bparrot@ti.com> + * + * 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/interrupt.h> +#include <linux/io.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> + +#include <media/v4l2-of.h> +#include <media/v4l2-async.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/v4l2-common.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> +#include "cal_regs.h" + +#define CAL_MODULE_NAME "cal" + +#define MAX_WIDTH 1920 +#define MAX_HEIGHT 1200 + +#define CAL_VERSION "0.1.0" + +MODULE_DESCRIPTION("TI CAL driver"); +MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(CAL_VERSION); + +static unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); + +static unsigned debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +/* timeperframe: min/max and default */ +static const struct v4l2_fract + tpf_default = {.numerator = 1001, .denominator = 30000}; + +#define cal_dbg(level, caldev, fmt, arg...) \ + v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) +#define cal_info(caldev, fmt, arg...) \ + v4l2_info(&caldev->v4l2_dev, fmt, ##arg) +#define cal_err(caldev, fmt, arg...) \ + v4l2_err(&caldev->v4l2_dev, fmt, ##arg) + +#define ctx_dbg(level, ctx, fmt, arg...) \ + v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) +#define ctx_info(ctx, fmt, arg...) \ + v4l2_info(&ctx->v4l2_dev, fmt, ##arg) +#define ctx_err(ctx, fmt, arg...) \ + v4l2_err(&ctx->v4l2_dev, fmt, ##arg) + +#define CAL_NUM_INPUT 1 +#define CAL_NUM_CONTEXT 2 + +#define bytes_per_line(pixel, bpp) (ALIGN(pixel * bpp, 16)) + +#define reg_read(dev, offset) ioread32(dev->base + offset) +#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) + +#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ + mask) +#define reg_write_field(dev, offset, field, mask) { \ + u32 val = reg_read(dev, offset); \ + set_field(&val, field, mask); \ + reg_write(dev, offset, val); } + +/* ------------------------------------------------------------------ + * Basic structures + * ------------------------------------------------------------------ + */ + +struct cal_fmt { + u32 fourcc; + u32 code; + u8 depth; +}; + +static struct cal_fmt cal_formats[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .depth = 24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .depth = 24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .depth = 32, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 16, + }, +}; + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* buffer for one video frame */ +struct cal_buffer { + /* common v4l buffer stuff -- must be first */ + struct vb2_v4l2_buffer vb; + struct list_head list; + const struct cal_fmt *fmt; +}; + +struct cal_dmaqueue { + struct list_head active; + + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +struct cm_data { + void __iomem *base; + struct resource *res; + + unsigned int camerrx_control; + + struct platform_device *pdev; +}; + +struct cc_data { + void __iomem *base; + struct resource *res; + + struct platform_device *pdev; +}; + +/* + * there is one cal_dev structure in the driver, it is shared by + * all instances. + */ +struct cal_dev { + int irq; + void __iomem *base; + struct resource *res; + struct platform_device *pdev; + struct v4l2_device v4l2_dev; + + /* Control Module handle */ + struct cm_data *cm; + /* Camera Core Module handle */ + struct cc_data *cc[CAL_NUM_CSI2_PORTS]; + + struct cal_ctx *ctx[CAL_NUM_CONTEXT]; +}; + +/* + * There is one cal_ctx structure for each camera core context. + */ +struct cal_ctx { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *sensor; + struct v4l2_of_endpoint endpoint; + + struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asd_list[1]; + + struct v4l2_fh fh; + struct cal_dev *dev; + struct cc_data *cc; + + /* v4l2_ioctl mutex */ + struct mutex mutex; + /* v4l2 buffers lock */ + spinlock_t slock; + + /* Several counters */ + unsigned long jiffies; + + struct vb2_alloc_ctx *alloc_ctx; + struct cal_dmaqueue vidq; + + /* Input Number */ + int input; + + /* video capture */ + const struct cal_fmt *fmt; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + /* Used to store current mbus frame format */ + struct v4l2_mbus_framefmt m_fmt; + + /* Current subdev enumerated format */ + struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; + int num_active_fmt; + + struct v4l2_fract timeperframe; + unsigned int sequence; + unsigned int external_rate; + struct vb2_queue vb_vidq; + unsigned int seq_count; + unsigned int csi2_port; + unsigned int virtual_channel; + + /* Pointer pointing to current v4l2_buffer */ + struct cal_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct cal_buffer *next_frm; +}; + +static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, + u32 pixelformat) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, + u32 code) +{ + const struct cal_fmt *fmt; + unsigned int k; + + for (k = 0; k < ctx->num_active_fmt; k++) { + fmt = ctx->active_fmt[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) +{ + return container_of(n, struct cal_ctx, notifier); +} + +static inline int get_field(u32 value, u32 mask) +{ + return (value & mask) >> __ffs(mask); +} + +static inline void set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +/* + * Control Module block access + */ +static struct cm_data *cm_create(struct cal_dev *dev) +{ + struct platform_device *pdev = dev->pdev; + struct cm_data *cm; + + cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL); + if (!cm) + return ERR_PTR(-ENOMEM); + + cm->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + cm->base = devm_ioremap_resource(&pdev->dev, cm->res); + if (IS_ERR(cm->base)) { + cal_err(dev, "failed to ioremap\n"); + return ERR_CAST(cm->base); + } + + cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cm->res->name, &cm->res->start, &cm->res->end); + + return cm; +} + +static void camerarx_phy_enable(struct cal_ctx *ctx) +{ + u32 val; + + if (!ctx->dev->cm->base) { + ctx_err(ctx, "cm not mapped\n"); + return; + } + + val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL); + if (ctx->csi2_port == 1) { + set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK); + set_field(&val, 0, CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK); + /* enable all lanes by default */ + set_field(&val, 0xf, CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK); + set_field(&val, 1, CM_CAMERRX_CTRL_CSI0_MODE_MASK); + } else if (ctx->csi2_port == 2) { + set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK); + set_field(&val, 0, CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK); + /* enable all lanes by default */ + set_field(&val, 0x3, CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK); + set_field(&val, 1, CM_CAMERRX_CTRL_CSI1_MODE_MASK); + } + reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val); +} + +static void camerarx_phy_disable(struct cal_ctx *ctx) +{ + u32 val; + + if (!ctx->dev->cm->base) { + ctx_err(ctx, "cm not mapped\n"); + return; + } + + val = reg_read(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL); + if (ctx->csi2_port == 1) + set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK); + else if (ctx->csi2_port == 2) + set_field(&val, 0x0, CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK); + reg_write(ctx->dev->cm, CM_CTRL_CORE_CAMERRX_CONTROL, val); +} + +/* + * Camera Instance access block + */ +static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) +{ + struct platform_device *pdev = dev->pdev; + struct cc_data *cc; + + cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); + if (!cc) + return ERR_PTR(-ENOMEM); + + cc->res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, + (core == 0) ? + "cal_rx_core0" : + "cal_rx_core1"); + cc->base = devm_ioremap_resource(&pdev->dev, cc->res); + if (IS_ERR(cc->base)) { + cal_err(dev, "failed to ioremap\n"); + return ERR_CAST(cc->base); + } + + cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + cc->res->name, &cc->res->start, &cc->res->end); + + return cc; +} + +/* + * Get Revision and HW info + */ +static void cal_get_hwinfo(struct cal_dev *dev) +{ + u32 revision = 0; + u32 hwinfo = 0; + + revision = reg_read(dev, CAL_HL_REVISION); + cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", + revision); + + hwinfo = reg_read(dev, CAL_HL_HWINFO); + cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", + hwinfo); +} + +static inline int cal_runtime_get(struct cal_dev *dev) +{ + int r; + + r = pm_runtime_get_sync(&dev->pdev->dev); + + return r; +} + +static inline void cal_runtime_put(struct cal_dev *dev) +{ + pm_runtime_put_sync(&dev->pdev->dev); +} + +static void cal_quickdump_regs(struct cal_dev *dev) +{ + cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)dev->base, + resource_size(dev->res), false); + + if (dev->ctx[0]) { + cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", + &dev->ctx[0]->cc->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)dev->ctx[0]->cc->base, + resource_size(dev->ctx[0]->cc->res), + false); + } + + if (dev->ctx[1]) { + cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", + &dev->ctx[1]->cc->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)dev->ctx[1]->cc->base, + resource_size(dev->ctx[1]->cc->res), + false); + } + + cal_info(dev, "CAMERRX_Control Registers @ %pa:\n", + &dev->cm->res->start); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, + (__force const void *)dev->cm->base, + resource_size(dev->cm->res), false); +} + +/* + * Enable the expected IRQ sources + */ +static void enable_irqs(struct cal_ctx *ctx) +{ + /* Enable IRQ_WDMA_END 0/1 */ + reg_write_field(ctx->dev, + CAL_HL_IRQENABLE_SET(2), + CAL_HL_IRQ_ENABLE, + CAL_HL_IRQ_MASK(ctx->csi2_port)); + /* Enable IRQ_WDMA_START 0/1 */ + reg_write_field(ctx->dev, + CAL_HL_IRQENABLE_SET(3), + CAL_HL_IRQ_ENABLE, + CAL_HL_IRQ_MASK(ctx->csi2_port)); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); +} + +static void disable_irqs(struct cal_ctx *ctx) +{ + /* Disable IRQ_WDMA_END 0/1 */ + reg_write_field(ctx->dev, + CAL_HL_IRQENABLE_CLR(2), + CAL_HL_IRQ_CLEAR, + CAL_HL_IRQ_MASK(ctx->csi2_port)); + /* Disable IRQ_WDMA_START 0/1 */ + reg_write_field(ctx->dev, + CAL_HL_IRQENABLE_CLR(3), + CAL_HL_IRQ_CLEAR, + CAL_HL_IRQ_MASK(ctx->csi2_port)); + /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ + reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); +} + +static void csi2_init(struct cal_ctx *ctx) +{ + int i; + u32 val; + + val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); + set_field(&val, CAL_GEN_ENABLE, + CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); + set_field(&val, CAL_GEN_ENABLE, + CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); + set_field(&val, CAL_GEN_DISABLE, + CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); + set_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); + reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); + + val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, + CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); + set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON, + CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); + reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + for (i = 0; i < 10; i++) { + if (reg_read_field(ctx->dev, + CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) == + CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON) + break; + usleep_range(1000, 1100); + } + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); + + val = reg_read(ctx->dev, CAL_CTRL); + set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); + set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); + set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); + set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); + reg_write(ctx->dev, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); +} + +static void csi2_lane_config(struct cal_ctx *ctx) +{ + u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); + u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; + u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; + struct v4l2_of_bus_mipi_csi2 *mipi_csi2 = &ctx->endpoint.bus.mipi_csi2; + int lane; + + set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); + for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { + /* + * Every lane are one nibble apart starting with the + * clock followed by the data lanes so shift masks by 4. + */ + lane_mask <<= 4; + polarity_mask <<= 4; + set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); + set_field(&val, mipi_csi2->lane_polarities[lane + 1], + polarity_mask); + } + + reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", + ctx->csi2_port, val); +} + +static void csi2_ppi_enable(struct cal_ctx *ctx) +{ + reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + CAL_GEN_ENABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static void csi2_ppi_disable(struct cal_ctx *ctx) +{ + reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), + CAL_GEN_DISABLE, CAL_CSI2_PPI_CTRL_IF_EN_MASK); +} + +static void csi2_ctx_config(struct cal_ctx *ctx) +{ + u32 val; + + val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); + set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); + /* + * DT type: MIPI CSI-2 Specs + * 0x1: All - DT filter is disabled + * 0x24: RGB888 1 pixel = 3 bytes + * 0x2B: RAW10 4 pixels = 5 bytes + * 0x2A: RAW8 1 pixel = 1 byte + * 0x1E: YUV422 2 pixels = 4 bytes + */ + set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); + /* Virtual Channel from the CSI2 sensor usually 0! */ + set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); + /* NUM_LINES_PER_FRAME => 0 means auto detect */ + set_field(&val, 0, CAL_CSI2_CTX_LINES_MASK); + set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); +} + +static void pix_proc_config(struct cal_ctx *ctx) +{ + u32 val; + + val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); + set_field(&val, CAL_PIX_PROC_EXTRACT_B8, CAL_PIX_PROC_EXTRACT_MASK); + set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + set_field(&val, CAL_PIX_PROC_PACK_B8, CAL_PIX_PROC_PACK_MASK); + set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); + set_field(&val, CAL_GEN_ENABLE, CAL_PIX_PROC_EN_MASK); + reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); +} + +static void cal_wr_dma_config(struct cal_ctx *ctx, + unsigned int width) +{ + u32 val; + + val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); + set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); + set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + set_field(&val, CAL_GEN_ENABLE, CAL_WR_DMA_CTRL_STALL_RD_MASK); + reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); + + /* + * width/16 not sure but giving it a whirl. + * zero does not work right + */ + reg_write_field(ctx->dev, + CAL_WR_DMA_OFST(ctx->csi2_port), + (width / 16), + CAL_WR_DMA_OFST_MASK); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); + + val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + /* 64 bit word means no skipping */ + set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + /* + * (width*8)/64 this should be size of an entire line + * in 64bit word but 0 means all data until the end + * is detected automagically + */ + set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, + reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); +} + +static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) +{ + reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); +} + +/* + * TCLK values are OK at their reset values + */ +#define TCLK_TERM 0 +#define TCLK_MISS 1 +#define TCLK_SETTLE 14 +#define THS_SETTLE 15 + +static void csi2_phy_config(struct cal_ctx *ctx) +{ + unsigned int reg0, reg1; + unsigned int ths_term, ths_settle; + unsigned int ddrclkperiod_us; + + /* + * THS_TERM: Programmed value = floor(20 ns/DDRClk period) - 2. + */ + ddrclkperiod_us = ctx->external_rate / 2000000; + ddrclkperiod_us = 1000000 / ddrclkperiod_us; + ctx_dbg(1, ctx, "ddrclkperiod_us: %d\n", ddrclkperiod_us); + + ths_term = 20000 / ddrclkperiod_us; + ths_term = (ths_term >= 2) ? ths_term - 2 : ths_term; + ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); + + /* + * THS_SETTLE: Programmed value = floor(176.3 ns/CtrlClk period) - 1. + * Since CtrlClk is fixed at 96Mhz then we get + * ths_settle = floor(176.3 / 10.416) - 1 = 15 + * If we ever switch to a dynamic clock then this code might be useful + * + * unsigned int ctrlclkperiod_us; + * ctrlclkperiod_us = 96000000 / 1000000; + * ctrlclkperiod_us = 1000000 / ctrlclkperiod_us; + * ctx_dbg(1, ctx, "ctrlclkperiod_us: %d\n", ctrlclkperiod_us); + + * ths_settle = 176300 / ctrlclkperiod_us; + * ths_settle = (ths_settle > 1) ? ths_settle - 1 : ths_settle; + */ + + ths_settle = THS_SETTLE; + ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); + + reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); + set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, + CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); + set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); + set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); + reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); + + reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); + set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); + set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); + set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); + set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); + + ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); + reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); +} + +static int cal_get_external_info(struct cal_ctx *ctx) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + ctx_err(ctx, "no pixel rate control in subdev: %s\n", + ctx->sensor->name); + return -EPIPE; + } + + ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); + ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); + + return 0; +} + +static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) +{ + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf; + unsigned long addr; + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + ctx->next_frm = buf; + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + cal_wr_dma_addr(ctx, addr); +} + +static inline void cal_process_buffer_complete(struct cal_ctx *ctx) +{ + ctx->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); + ctx->cur_frm->vb.field = ctx->m_fmt.field; + ctx->cur_frm->vb.sequence = ctx->sequence++; + + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); + ctx->cur_frm = ctx->next_frm; +} + +#define isvcirqset(irq, vc, ff) (irq & \ + (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) + +#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) + +static irqreturn_t cal_irq(int irq_cal, void *data) +{ + struct cal_dev *dev = (struct cal_dev *)data; + struct cal_ctx *ctx; + struct cal_dmaqueue *dma_q; + u32 irqst2, irqst3; + + /* Check which DMA just finished */ + irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); + if (irqst2) { + /* Clear Interrupt status */ + reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + + /* Need to check both port */ + if (isportirqset(irqst2, 1)) { + ctx = dev->ctx[0]; + + if (ctx->cur_frm != ctx->next_frm) + cal_process_buffer_complete(ctx); + } + + if (isportirqset(irqst2, 2)) { + ctx = dev->ctx[1]; + + if (ctx->cur_frm != ctx->next_frm) + cal_process_buffer_complete(ctx); + } + } + + /* Check which DMA just started */ + irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); + if (irqst3) { + /* Clear Interrupt status */ + reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); + + /* Need to check both port */ + if (isportirqset(irqst3, 1)) { + ctx = dev->ctx[0]; + dma_q = &ctx->vidq; + + spin_lock(&ctx->slock); + if (!list_empty(&dma_q->active) && + ctx->cur_frm == ctx->next_frm) + cal_schedule_next_buffer(ctx); + spin_unlock(&ctx->slock); + } + + if (isportirqset(irqst3, 2)) { + ctx = dev->ctx[1]; + dma_q = &ctx->vidq; + + spin_lock(&ctx->slock); + if (!list_empty(&dma_q->active) && + ctx->cur_frm == ctx->next_frm) + cal_schedule_next_buffer(ctx); + spin_unlock(&ctx->slock); + } + } + + return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int cal_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cal_ctx *ctx = video_drvdata(file); + + strlcpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", ctx->v4l2_dev.name); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int cal_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt = NULL; + + if (f->index >= ctx->num_active_fmt) + return -EINVAL; + + fmt = ctx->active_fmt[f->index]; + + f->pixelformat = fmt->fourcc; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + return 0; +} + +static int __subdev_get_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + if (!ctx->sensor) + return -EINVAL; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + *fmt = *mbus_fmt; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct cal_ctx *ctx, + struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + int ret; + + if (!ctx->sensor) + return -EINVAL; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + *mbus_fmt = *fmt; + + ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); + if (ret) + return ret; + + ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int cal_calc_format_size(struct cal_ctx *ctx, + const struct cal_fmt *fmt, + struct v4l2_format *f) +{ + if (!fmt) { + ctx_dbg(3, ctx, "No cal_fmt provided!\n"); + return -EINVAL; + } + + v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2, + &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0); + f->fmt.pix.bytesperline = bytes_per_line(f->fmt.pix.width, + fmt->depth >> 3); + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", + __func__, fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static int cal_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *f = ctx->v_fmt; + + return 0; +} + +static int cal_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret, found; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + if (!fmt) { + ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt = ctx->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, + NULL, &fse); + if (ret) + break; + + if ((f->fmt.pix.width == fse.max_width) && + (f->fmt.pix.height == fse.max_height)) { + found = true; + break; + } else if ((f->fmt.pix.width >= fse.min_width) && + (f->fmt.pix.width <= fse.max_width) && + (f->fmt.pix.height >= fse.min_height) && + (f->fmt.pix.height <= fse.max_height)) { + found = true; + break; + } + } + + if (!found) { + /* use existing values as default */ + f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; + f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; + } + + /* + * Use current colorspace for now, it will get + * updated properly during s_fmt + */ + f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; + return cal_calc_format_size(ctx, fmt, f); +} + +static int cal_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cal_ctx *ctx = video_drvdata(file); + struct vb2_queue *q = &ctx->vb_vidq; + const struct cal_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + if (vb2_is_busy(q)) { + ctx_dbg(3, ctx, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = cal_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + + ret = __subdev_set_format(ctx, &mbus_fmt); + if (ret) + return ret; + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != fmt->code) { + ctx_dbg(3, ctx, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + *f = ctx->v_fmt; + + return 0; +} + +static int cal_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmt = find_format_by_pix(ctx, fsize->pixel_format); + if (!fmt) { + ctx_dbg(3, ctx, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fse.index = fsize->index; + fse.pad = 0; + fse.code = fmt->code; + + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); + if (ret) + return -EINVAL; + + ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int cal_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index >= CAL_NUM_INPUT) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + sprintf(inp->name, "Camera %u", inp->index); + return 0; +} + +static int cal_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cal_ctx *ctx = video_drvdata(file); + + *i = ctx->input; + return 0; +} + +static int cal_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cal_ctx *ctx = video_drvdata(file); + + if (i >= CAL_NUM_INPUT) + return -EINVAL; + + ctx->input = i; + return 0; +} + +/* timeperframe is arbitrary and continuous */ +static int cal_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct cal_ctx *ctx = video_drvdata(file); + const struct cal_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + if (fival->index) + return -EINVAL; + + fmt = find_format_by_pix(ctx, fival->pixel_format); + if (!fmt) + return -EINVAL; + + /* check for valid width/height */ + ret = 0; + fse.pad = 0; + fse.code = fmt->code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + for (fse.index = 0; ; fse.index++) { + ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, + NULL, &fse); + if (ret) + return -EINVAL; + + if ((fival->width == fse.max_width) && + (fival->height == fse.max_height)) + break; + else if ((fival->width >= fse.min_width) && + (fival->width <= fse.max_width) && + (fival->height >= fse.min_height) && + (fival->height <= fse.max_height)) + break; + + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = 30; + + return 0; +} + +/* + * Videobuf operations + */ +static int cal_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + unsigned size = ctx->v_fmt.fmt.pix.sizeimage; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + alloc_ctxs[0] = ctx->alloc_ctx; + + if (*nplanes) { + if (sizes[0] < size) + return -EINVAL; + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); + + return 0; +} + +static int cal_buffer_prepare(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + unsigned long size; + + if (WARN_ON(!ctx->fmt)) + return -EINVAL; + + size = ctx->v_fmt.fmt.pix.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + ctx_err(ctx, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void cal_buffer_queue(struct vb2_buffer *vb) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct cal_buffer *buf = container_of(vb, struct cal_buffer, + vb.vb2_buf); + struct cal_dmaqueue *vidq = &ctx->vidq; + unsigned long flags = 0; + + /* recheck locking */ + spin_lock_irqsave(&ctx->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&ctx->slock, flags); +} + +static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long addr = 0; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ctx->slock, flags); + if (list_empty(&dma_q->active)) { + spin_unlock_irqrestore(&ctx->slock, flags); + ctx_dbg(3, ctx, "buffer queue is empty\n"); + return -EIO; + } + + buf = list_entry(dma_q->active.next, struct cal_buffer, list); + ctx->cur_frm = buf; + ctx->next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&ctx->slock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); + ctx->sequence = 0; + + ret = cal_get_external_info(ctx); + if (ret < 0) + goto err; + + cal_runtime_get(ctx->dev); + + enable_irqs(ctx); + camerarx_phy_enable(ctx); + csi2_init(ctx); + csi2_phy_config(ctx); + csi2_lane_config(ctx); + csi2_ctx_config(ctx); + pix_proc_config(ctx); + cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline); + cal_wr_dma_addr(ctx, addr); + csi2_ppi_enable(ctx); + + if (ctx->sensor) { + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 1)) { + ctx_err(ctx, "stream on failed in subdev\n"); + cal_runtime_put(ctx->dev); + ret = -EINVAL; + goto err; + } + } + + if (debug >= 4) + cal_quickdump_regs(ctx->dev); + + return 0; + +err: + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + return ret; +} + +static void cal_stop_streaming(struct vb2_queue *vq) +{ + struct cal_ctx *ctx = vb2_get_drv_priv(vq); + struct cal_dmaqueue *dma_q = &ctx->vidq; + struct cal_buffer *buf, *tmp; + unsigned long flags; + + if (ctx->sensor) { + if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) + ctx_err(ctx, "stream off failed in subdev\n"); + } + + csi2_ppi_disable(ctx); + disable_irqs(ctx); + + /* Release all active buffers */ + spin_lock_irqsave(&ctx->slock, flags); + list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (ctx->cur_frm == ctx->next_frm) { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } else { + vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + ctx->cur_frm = NULL; + ctx->next_frm = NULL; + spin_unlock_irqrestore(&ctx->slock, flags); + + cal_runtime_put(ctx->dev); +} + +static struct vb2_ops cal_video_qops = { + .queue_setup = cal_queue_setup, + .buf_prepare = cal_buffer_prepare, + .buf_queue = cal_buffer_queue, + .start_streaming = cal_start_streaming, + .stop_streaming = cal_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct v4l2_file_operations cal_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = vb2_fop_mmap, +}; + +static const struct v4l2_ioctl_ops cal_ioctl_ops = { + .vidioc_querycap = cal_querycap, + .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, + .vidioc_enum_framesizes = cal_enum_framesizes, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_enum_input = cal_enum_input, + .vidioc_g_input = cal_g_input, + .vidioc_s_input = cal_s_input, + .vidioc_enum_frameintervals = cal_enum_frameintervals, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device cal_videodev = { + .name = CAL_MODULE_NAME, + .fops = &cal_fops, + .ioctl_ops = &cal_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +/* ----------------------------------------------------------------- + * Initialization and module stuff + * ------------------------------------------------------------------ + */ +static int cal_complete_ctx(struct cal_ctx *ctx); + +static int cal_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct cal_ctx *ctx = notifier_to_ctx(notifier); + struct v4l2_subdev_mbus_code_enum mbus_code; + int ret = 0; + int i, j, k; + + if (ctx->sensor) { + ctx_info(ctx, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + ctx->sensor = subdev; + ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); + + /* Enumerate sub device formats and enable all matching local formats */ + ctx->num_active_fmt = 0; + for (j = 0, i = 0; ret != -EINVAL; ++j) { + struct cal_fmt *fmt; + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + ctx_dbg(2, ctx, + "subdev %s: code: %04x idx: %d\n", + subdev->name, mbus_code.code, j); + + for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { + fmt = &cal_formats[k]; + + if (mbus_code.code == fmt->code) { + ctx->active_fmt[i] = fmt; + ctx_dbg(2, ctx, + "matched fourcc: %s: code: %04x idx: %d\n", + fourcc_to_str(fmt->fourcc), + fmt->code, i); + ctx->num_active_fmt = ++i; + } + } + } + + if (i == 0) { + ctx_err(ctx, "No suitable format reported by subdev %s\n", + subdev->name); + return -EINVAL; + } + + cal_complete_ctx(ctx); + + return 0; +} + +static int cal_async_complete(struct v4l2_async_notifier *notifier) +{ + struct cal_ctx *ctx = notifier_to_ctx(notifier); + const struct cal_fmt *fmt; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + ret = __subdev_get_format(ctx, &mbus_fmt); + if (ret) + return ret; + + fmt = find_format_by_code(ctx, mbus_fmt.code); + if (!fmt) { + ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", + mbus_fmt.code); + return -EINVAL; + } + + /* Save current subdev format */ + v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); + ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + cal_calc_format_size(ctx, fmt, &ctx->v_fmt); + ctx->fmt = fmt; + ctx->m_fmt = mbus_fmt; + + return 0; +} + +static int cal_complete_ctx(struct cal_ctx *ctx) +{ + struct video_device *vfd; + struct vb2_queue *q; + int ret; + + ctx->timeperframe = tpf_default; + ctx->external_rate = 192000000; + + /* initialize locks */ + spin_lock_init(&ctx->slock); + mutex_init(&ctx->mutex); + + /* initialize queue */ + q = &ctx->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = ctx; + q->buf_struct_size = sizeof(struct cal_buffer); + q->ops = &cal_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ctx->mutex; + q->min_buffers_needed = 3; + + ret = vb2_queue_init(q); + if (ret) + return ret; + + /* init video dma queues */ + INIT_LIST_HEAD(&ctx->vidq.active); + + vfd = &ctx->vdev; + *vfd = cal_videodev; + vfd->v4l2_dev = &ctx->v4l2_dev; + vfd->queue = q; + + /* + * Provide a mutex to v4l2 core. It will be used to protect + * all fops and v4l2 ioctls. + */ + vfd->lock = &ctx->mutex; + video_set_drvdata(vfd, ctx); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); + if (ret < 0) + return ret; + + v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev); + if (IS_ERR(ctx->alloc_ctx)) { + ctx_err(ctx, "Failed to alloc vb2 context\n"); + ret = PTR_ERR(ctx->alloc_ctx); + goto vdev_unreg; + } + + return 0; + +vdev_unreg: + video_unregister_device(vfd); + return ret; +} + +static struct device_node * +of_get_next_port(const struct device_node *parent, + struct device_node *prev) +{ + struct device_node *port = NULL; + + if (!parent) + return NULL; + + if (!prev) { + struct device_node *ports; + /* + * It's the first call, we have to find a port subnode + * within this node or within an optional 'ports' node. + */ + ports = of_get_child_by_name(parent, "ports"); + if (ports) + parent = ports; + + port = of_get_child_by_name(parent, "port"); + + /* release the 'ports' node */ + of_node_put(ports); + } else { + struct device_node *ports; + + ports = of_get_parent(prev); + if (!ports) + return NULL; + + do { + port = of_get_next_child(ports, prev); + if (!port) { + of_node_put(ports); + return NULL; + } + prev = port; + } while (of_node_cmp(port->name, "port") != 0); + } + + return port; +} + +static struct device_node * +of_get_next_endpoint(const struct device_node *parent, + struct device_node *prev) +{ + struct device_node *ep = NULL; + + if (!parent) + return NULL; + + do { + ep = of_get_next_child(parent, prev); + if (!ep) + return NULL; + prev = ep; + } while (of_node_cmp(ep->name, "endpoint") != 0); + + return ep; +} + +static int of_cal_create_instance(struct cal_ctx *ctx, int inst) +{ + struct platform_device *pdev = ctx->dev->pdev; + struct device_node *ep_node, *port, *remote_ep, + *sensor_node, *parent; + struct v4l2_of_endpoint *endpoint; + struct v4l2_async_subdev *asd; + u32 regval = 0; + int ret, index, found_port = 0, lane; + + parent = pdev->dev.of_node; + + asd = &ctx->asd; + endpoint = &ctx->endpoint; + + ep_node = NULL; + port = NULL; + remote_ep = NULL; + sensor_node = NULL; + ret = -EINVAL; + + ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); + for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { + port = of_get_next_port(parent, port); + if (!port) { + ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", + index); + goto cleanup_exit; + } + + /* Match the slice number with <REG> */ + of_property_read_u32(port, "reg", ®val); + ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n", + index, inst, regval); + if ((regval == inst) && (index == inst)) { + found_port = 1; + break; + } + } + + if (!found_port) { + ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", + inst); + goto cleanup_exit; + } + + ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", + inst); + + ep_node = of_get_next_endpoint(port, ep_node); + if (!ep_node) { + ctx_dbg(3, ctx, "can't get next endpoint\n"); + goto cleanup_exit; + } + + sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!sensor_node) { + ctx_dbg(3, ctx, "can't get remote parent\n"); + goto cleanup_exit; + } + asd->match_type = V4L2_ASYNC_MATCH_OF; + asd->match.of.node = sensor_node; + + remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0); + if (!remote_ep) { + ctx_dbg(3, ctx, "can't get remote-endpoint\n"); + goto cleanup_exit; + } + v4l2_of_parse_endpoint(remote_ep, endpoint); + + if (endpoint->bus_type != V4L2_MBUS_CSI2) { + ctx_err(ctx, "Port:%d sub-device %s is not a CSI2 device\n", + inst, sensor_node->name); + goto cleanup_exit; + } + + /* Store Virtual Channel number */ + ctx->virtual_channel = endpoint->base.id; + + ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); + ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); + ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); + ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); + ctx_dbg(3, ctx, "num_data_lanes=%d\n", + endpoint->bus.mipi_csi2.num_data_lanes); + ctx_dbg(3, ctx, "data_lanes= <\n"); + for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) + ctx_dbg(3, ctx, "\t%d\n", + endpoint->bus.mipi_csi2.data_lanes[lane]); + ctx_dbg(3, ctx, "\t>\n"); + + ctx_dbg(1, ctx, "Port: %d found sub-device %s\n", + inst, sensor_node->name); + + ctx->asd_list[0] = asd; + ctx->notifier.subdevs = ctx->asd_list; + ctx->notifier.num_subdevs = 1; + ctx->notifier.bound = cal_async_bound; + ctx->notifier.complete = cal_async_complete; + ret = v4l2_async_notifier_register(&ctx->v4l2_dev, + &ctx->notifier); + if (ret) { + ctx_err(ctx, "Error registering async notifier\n"); + ret = -EINVAL; + } + +cleanup_exit: + if (!remote_ep) + of_node_put(remote_ep); + if (!sensor_node) + of_node_put(sensor_node); + if (!ep_node) + of_node_put(ep_node); + if (!port) + of_node_put(port); + + return ret; +} + +static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) +{ + struct cal_ctx *ctx; + struct v4l2_ctrl_handler *hdl; + int ret; + + ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return NULL; + + /* save the cal_dev * for future ref */ + ctx->dev = dev; + + snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), + "%s-%03d", CAL_MODULE_NAME, inst); + ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); + if (ret) + goto err_exit; + + hdl = &ctx->ctrl_handler; + ret = v4l2_ctrl_handler_init(hdl, 11); + if (ret) { + ctx_err(ctx, "Failed to init ctrl handler\n"); + goto unreg_dev; + } + ctx->v4l2_dev.ctrl_handler = hdl; + + /* Make sure Camera Core H/W register area is available */ + ctx->cc = dev->cc[inst]; + + /* Store the instance id */ + ctx->csi2_port = inst + 1; + + ret = of_cal_create_instance(ctx, inst); + if (ret) { + ret = -EINVAL; + goto free_hdl; + } + return ctx; + +free_hdl: + v4l2_ctrl_handler_free(hdl); +unreg_dev: + v4l2_device_unregister(&ctx->v4l2_dev); +err_exit: + return NULL; +} + +static int cal_probe(struct platform_device *pdev) +{ + struct cal_dev *dev; + int ret; + int irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* set pseudo v4l2 device name so we can use v4l2_printk */ + strlcpy(dev->v4l2_dev.name, CAL_MODULE_NAME, + sizeof(dev->v4l2_dev.name)); + + /* save pdev pointer */ + dev->pdev = pdev; + + dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "cal_top"); + dev->base = devm_ioremap_resource(&pdev->dev, dev->res); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", + dev->res->name, &dev->res->start, &dev->res->end); + + irq = platform_get_irq(pdev, 0); + cal_dbg(1, dev, "got irq# %d\n", irq); + ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, + dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dev); + + dev->cm = cm_create(dev); + if (IS_ERR(dev->cm)) + return PTR_ERR(dev->cm); + + dev->cc[0] = cc_create(dev, 0); + if (IS_ERR(dev->cc[0])) + return PTR_ERR(dev->cc[0]); + + dev->cc[1] = cc_create(dev, 1); + if (IS_ERR(dev->cc[1])) + return PTR_ERR(dev->cc[1]); + + dev->ctx[0] = NULL; + dev->ctx[1] = NULL; + + dev->ctx[0] = cal_create_instance(dev, 0); + dev->ctx[1] = cal_create_instance(dev, 1); + if (!dev->ctx[0] && !dev->ctx[1]) { + cal_err(dev, "Neither port is configured, no point in staying up\n"); + return -ENODEV; + } + + pm_runtime_enable(&pdev->dev); + + ret = cal_runtime_get(dev); + if (ret) + goto runtime_disable; + + /* Just check we can actually access the module */ + cal_get_hwinfo(dev); + + cal_runtime_put(dev); + + return 0; + +runtime_disable: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int cal_remove(struct platform_device *pdev) +{ + struct cal_dev *dev = + (struct cal_dev *)platform_get_drvdata(pdev); + struct cal_ctx *ctx; + int i; + + cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); + + cal_runtime_get(dev); + + for (i = 0; i < CAL_NUM_CONTEXT; i++) { + ctx = dev->ctx[i]; + if (ctx) { + ctx_dbg(1, ctx, "unregistering %s\n", + video_device_node_name(&ctx->vdev)); + camerarx_phy_disable(ctx); + v4l2_async_notifier_unregister(&ctx->notifier); + vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_device_unregister(&ctx->v4l2_dev); + video_unregister_device(&ctx->vdev); + } + } + + cal_runtime_put(dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id cal_of_match[] = { + { .compatible = "ti,dra72-cal", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cal_of_match); +#endif + +static struct platform_driver cal_pdrv = { + .probe = cal_probe, + .remove = cal_remove, + .driver = { + .name = CAL_MODULE_NAME, + .of_match_table = of_match_ptr(cal_of_match), + }, +}; + +module_platform_driver(cal_pdrv); diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti-vpe/cal_regs.h new file mode 100644 index 000000000000..82b3dcf87128 --- /dev/null +++ b/drivers/media/platform/ti-vpe/cal_regs.h @@ -0,0 +1,479 @@ +/* + * TI CAL camera interface driver + * + * Copyright (c) 2015 Texas Instruments Inc. + * + * Benoit Parrot, <bparrot@ti.com> + * + * 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. + */ + +#ifndef __TI_CAL_REGS_H +#define __TI_CAL_REGS_H + +#define CAL_NUM_CSI2_PORTS 2 + +/* CAL register offsets */ + +#define CAL_HL_REVISION 0x0000 +#define CAL_HL_HWINFO 0x0004 +#define CAL_HL_SYSCONFIG 0x0010 +#define CAL_HL_IRQ_EOI 0x001c +#define CAL_HL_IRQSTATUS_RAW(m) (0x20U + ((m-1) * 0x10U)) +#define CAL_HL_IRQSTATUS(m) (0x24U + ((m-1) * 0x10U)) +#define CAL_HL_IRQENABLE_SET(m) (0x28U + ((m-1) * 0x10U)) +#define CAL_HL_IRQENABLE_CLR(m) (0x2cU + ((m-1) * 0x10U)) +#define CAL_PIX_PROC(m) (0xc0U + ((m-1) * 0x4U)) +#define CAL_CTRL 0x100 +#define CAL_CTRL1 0x104 +#define CAL_LINE_NUMBER_EVT 0x108 +#define CAL_VPORT_CTRL1 0x120 +#define CAL_VPORT_CTRL2 0x124 +#define CAL_BYS_CTRL1 0x130 +#define CAL_BYS_CTRL2 0x134 +#define CAL_RD_DMA_CTRL 0x140 +#define CAL_RD_DMA_PIX_ADDR 0x144 +#define CAL_RD_DMA_PIX_OFST 0x148 +#define CAL_RD_DMA_XSIZE 0x14c +#define CAL_RD_DMA_YSIZE 0x150 +#define CAL_RD_DMA_INIT_ADDR 0x154 +#define CAL_RD_DMA_INIT_OFST 0x168 +#define CAL_RD_DMA_CTRL2 0x16c +#define CAL_WR_DMA_CTRL(m) (0x200U + ((m-1) * 0x10U)) +#define CAL_WR_DMA_ADDR(m) (0x204U + ((m-1) * 0x10U)) +#define CAL_WR_DMA_OFST(m) (0x208U + ((m-1) * 0x10U)) +#define CAL_WR_DMA_XSIZE(m) (0x20cU + ((m-1) * 0x10U)) +#define CAL_CSI2_PPI_CTRL(m) (0x300U + ((m-1) * 0x80U)) +#define CAL_CSI2_COMPLEXIO_CFG(m) (0x304U + ((m-1) * 0x80U)) +#define CAL_CSI2_COMPLEXIO_IRQSTATUS(m) (0x308U + ((m-1) * 0x80U)) +#define CAL_CSI2_SHORT_PACKET(m) (0x30cU + ((m-1) * 0x80U)) +#define CAL_CSI2_COMPLEXIO_IRQENABLE(m) (0x310U + ((m-1) * 0x80U)) +#define CAL_CSI2_TIMING(m) (0x314U + ((m-1) * 0x80U)) +#define CAL_CSI2_VC_IRQENABLE(m) (0x318U + ((m-1) * 0x80U)) +#define CAL_CSI2_VC_IRQSTATUS(m) (0x328U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX0(m) (0x330U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX1(m) (0x334U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX2(m) (0x338U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX3(m) (0x33cU + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX4(m) (0x340U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX5(m) (0x344U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX6(m) (0x348U + ((m-1) * 0x80U)) +#define CAL_CSI2_CTX7(m) (0x34cU + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS0(m) (0x350U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS1(m) (0x354U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS2(m) (0x358U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS3(m) (0x35cU + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS4(m) (0x360U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS5(m) (0x364U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS6(m) (0x368U + ((m-1) * 0x80U)) +#define CAL_CSI2_STATUS7(m) (0x36cU + ((m-1) * 0x80U)) + +/* CAL CSI2 PHY register offsets */ +#define CAL_CSI2_PHY_REG0 0x000 +#define CAL_CSI2_PHY_REG1 0x004 +#define CAL_CSI2_PHY_REG2 0x008 + +/* CAL Control Module Core Camerrx Control register offsets */ +#define CM_CTRL_CORE_CAMERRX_CONTROL 0x000 + +/********************************************************************* +* Generic value used in various field below +*********************************************************************/ + +#define CAL_GEN_DISABLE 0 +#define CAL_GEN_ENABLE 1 +#define CAL_GEN_FALSE 0 +#define CAL_GEN_TRUE 1 + +/********************************************************************* +* Field Definition Macros +*********************************************************************/ + +#define CAL_HL_REVISION_MINOR_MASK GENMASK(5, 0) +#define CAL_HL_REVISION_CUSTOM_MASK GENMASK(7, 6) +#define CAL_HL_REVISION_MAJOR_MASK GENMASK(10, 8) +#define CAL_HL_REVISION_RTL_MASK GENMASK(15, 11) +#define CAL_HL_REVISION_FUNC_MASK GENMASK(27, 16) +#define CAL_HL_REVISION_SCHEME_MASK GENMASK(31, 30) +#define CAL_HL_REVISION_SCHEME_H08 1 +#define CAL_HL_REVISION_SCHEME_LEGACY 0 + +#define CAL_HL_HWINFO_WFIFO_MASK GENMASK(3, 0) +#define CAL_HL_HWINFO_RFIFO_MASK GENMASK(7, 4) +#define CAL_HL_HWINFO_PCTX_MASK GENMASK(12, 8) +#define CAL_HL_HWINFO_WCTX_MASK GENMASK(18, 13) +#define CAL_HL_HWINFO_VFIFO_MASK GENMASK(22, 19) +#define CAL_HL_HWINFO_NCPORT_MASK GENMASK(27, 23) +#define CAL_HL_HWINFO_NPPI_CTXS0_MASK GENMASK(29, 28) +#define CAL_HL_HWINFO_NPPI_CTXS1_MASK GENMASK(31, 30) +#define CAL_HL_HWINFO_NPPI_CONTEXTS_ZERO 0 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_FOUR 1 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_EIGHT 2 +#define CAL_HL_HWINFO_NPPI_CONTEXTS_RESERVED 3 + +#define CAL_HL_SYSCONFIG_SOFTRESET_MASK BIT_MASK(0) +#define CAL_HL_SYSCONFIG_SOFTRESET_DONE 0x0 +#define CAL_HL_SYSCONFIG_SOFTRESET_PENDING 0x1 +#define CAL_HL_SYSCONFIG_SOFTRESET_NOACTION 0x0 +#define CAL_HL_SYSCONFIG_SOFTRESET_RESET 0x1 +#define CAL_HL_SYSCONFIG_IDLE_MASK GENMASK(3, 2) +#define CAL_HL_SYSCONFIG_IDLEMODE_FORCE 0 +#define CAL_HL_SYSCONFIG_IDLEMODE_NO 1 +#define CAL_HL_SYSCONFIG_IDLEMODE_SMART1 2 +#define CAL_HL_SYSCONFIG_IDLEMODE_SMART2 3 + +#define CAL_HL_IRQ_EOI_LINE_NUMBER_MASK BIT_MASK(0) +#define CAL_HL_IRQ_EOI_LINE_NUMBER_READ0 0 +#define CAL_HL_IRQ_EOI_LINE_NUMBER_EOI0 0 + +#define CAL_HL_IRQ_MASK(m) BIT_MASK(m-1) +#define CAL_HL_IRQ_NOACTION 0x0 +#define CAL_HL_IRQ_ENABLE 0x1 +#define CAL_HL_IRQ_CLEAR 0x1 +#define CAL_HL_IRQ_DISABLED 0x0 +#define CAL_HL_IRQ_ENABLED 0x1 +#define CAL_HL_IRQ_PENDING 0x1 + +#define CAL_PIX_PROC_EN_MASK BIT_MASK(0) +#define CAL_PIX_PROC_EXTRACT_MASK GENMASK(4, 1) +#define CAL_PIX_PROC_EXTRACT_B6 0x0 +#define CAL_PIX_PROC_EXTRACT_B7 0x1 +#define CAL_PIX_PROC_EXTRACT_B8 0x2 +#define CAL_PIX_PROC_EXTRACT_B10 0x3 +#define CAL_PIX_PROC_EXTRACT_B10_MIPI 0x4 +#define CAL_PIX_PROC_EXTRACT_B12 0x5 +#define CAL_PIX_PROC_EXTRACT_B12_MIPI 0x6 +#define CAL_PIX_PROC_EXTRACT_B14 0x7 +#define CAL_PIX_PROC_EXTRACT_B14_MIPI 0x8 +#define CAL_PIX_PROC_EXTRACT_B16_BE 0x9 +#define CAL_PIX_PROC_EXTRACT_B16_LE 0xa +#define CAL_PIX_PROC_DPCMD_MASK GENMASK(9, 5) +#define CAL_PIX_PROC_DPCMD_BYPASS 0x0 +#define CAL_PIX_PROC_DPCMD_DPCM_10_8_1 0x2 +#define CAL_PIX_PROC_DPCMD_DPCM_12_8_1 0x8 +#define CAL_PIX_PROC_DPCMD_DPCM_10_7_1 0x4 +#define CAL_PIX_PROC_DPCMD_DPCM_10_7_2 0x5 +#define CAL_PIX_PROC_DPCMD_DPCM_10_6_1 0x6 +#define CAL_PIX_PROC_DPCMD_DPCM_10_6_2 0x7 +#define CAL_PIX_PROC_DPCMD_DPCM_12_7_1 0xa +#define CAL_PIX_PROC_DPCMD_DPCM_12_6_1 0xc +#define CAL_PIX_PROC_DPCMD_DPCM_14_10 0xe +#define CAL_PIX_PROC_DPCMD_DPCM_14_8_1 0x10 +#define CAL_PIX_PROC_DPCMD_DPCM_16_12_1 0x12 +#define CAL_PIX_PROC_DPCMD_DPCM_16_10_1 0x14 +#define CAL_PIX_PROC_DPCMD_DPCM_16_8_1 0x16 +#define CAL_PIX_PROC_DPCME_MASK GENMASK(15, 11) +#define CAL_PIX_PROC_DPCME_BYPASS 0x0 +#define CAL_PIX_PROC_DPCME_DPCM_10_8_1 0x2 +#define CAL_PIX_PROC_DPCME_DPCM_12_8_1 0x8 +#define CAL_PIX_PROC_DPCME_DPCM_14_10 0xe +#define CAL_PIX_PROC_DPCME_DPCM_14_8_1 0x10 +#define CAL_PIX_PROC_DPCME_DPCM_16_12_1 0x12 +#define CAL_PIX_PROC_DPCME_DPCM_16_10_1 0x14 +#define CAL_PIX_PROC_DPCME_DPCM_16_8_1 0x16 +#define CAL_PIX_PROC_PACK_MASK GENMASK(18, 16) +#define CAL_PIX_PROC_PACK_B8 0x0 +#define CAL_PIX_PROC_PACK_B10_MIPI 0x2 +#define CAL_PIX_PROC_PACK_B12 0x3 +#define CAL_PIX_PROC_PACK_B12_MIPI 0x4 +#define CAL_PIX_PROC_PACK_B16 0x5 +#define CAL_PIX_PROC_PACK_ARGB 0x6 +#define CAL_PIX_PROC_CPORT_MASK GENMASK(23, 19) + +#define CAL_CTRL_POSTED_WRITES_MASK BIT_MASK(0) +#define CAL_CTRL_POSTED_WRITES_NONPOSTED 0 +#define CAL_CTRL_POSTED_WRITES 1 +#define CAL_CTRL_TAGCNT_MASK GENMASK(4, 1) +#define CAL_CTRL_BURSTSIZE_MASK GENMASK(6, 5) +#define CAL_CTRL_BURSTSIZE_BURST16 0x0 +#define CAL_CTRL_BURSTSIZE_BURST32 0x1 +#define CAL_CTRL_BURSTSIZE_BURST64 0x2 +#define CAL_CTRL_BURSTSIZE_BURST128 0x3 +#define CAL_CTRL_LL_FORCE_STATE_MASK GENMASK(12, 7) +#define CAL_CTRL_MFLAGL_MASK GENMASK(20, 13) +#define CAL_CTRL_PWRSCPCLK_MASK BIT_MASK(21) +#define CAL_CTRL_PWRSCPCLK_AUTO 0 +#define CAL_CTRL_PWRSCPCLK_FORCE 1 +#define CAL_CTRL_RD_DMA_STALL_MASK BIT_MASK(22) +#define CAL_CTRL_MFLAGH_MASK GENMASK(31, 24) + +#define CAL_CTRL1_PPI_GROUPING_MASK GENMASK(1, 0) +#define CAL_CTRL1_PPI_GROUPING_DISABLED 0 +#define CAL_CTRL1_PPI_GROUPING_RESERVED 1 +#define CAL_CTRL1_PPI_GROUPING_0 2 +#define CAL_CTRL1_PPI_GROUPING_1 3 +#define CAL_CTRL1_INTERLEAVE01_MASK GENMASK(3, 2) +#define CAL_CTRL1_INTERLEAVE01_DISABLED 0 +#define CAL_CTRL1_INTERLEAVE01_PIX1 1 +#define CAL_CTRL1_INTERLEAVE01_PIX4 2 +#define CAL_CTRL1_INTERLEAVE01_RESERVED 3 +#define CAL_CTRL1_INTERLEAVE23_MASK GENMASK(5, 4) +#define CAL_CTRL1_INTERLEAVE23_DISABLED 0 +#define CAL_CTRL1_INTERLEAVE23_PIX1 1 +#define CAL_CTRL1_INTERLEAVE23_PIX4 2 +#define CAL_CTRL1_INTERLEAVE23_RESERVED 3 + +#define CAL_LINE_NUMBER_EVT_CPORT_MASK GENMASK(4, 0) +#define CAL_LINE_NUMBER_EVT_MASK GENMASK(29, 16) + +#define CAL_VPORT_CTRL1_PCLK_MASK GENMASK(16, 0) +#define CAL_VPORT_CTRL1_XBLK_MASK GENMASK(24, 17) +#define CAL_VPORT_CTRL1_YBLK_MASK GENMASK(30, 25) +#define CAL_VPORT_CTRL1_WIDTH_MASK BIT_MASK(31) +#define CAL_VPORT_CTRL1_WIDTH_ONE 0 +#define CAL_VPORT_CTRL1_WIDTH_TWO 1 + +#define CAL_VPORT_CTRL2_CPORT_MASK GENMASK(4, 0) +#define CAL_VPORT_CTRL2_FREERUNNING_MASK BIT_MASK(15) +#define CAL_VPORT_CTRL2_FREERUNNING_GATED 0 +#define CAL_VPORT_CTRL2_FREERUNNING_FREE 1 +#define CAL_VPORT_CTRL2_FS_RESETS_MASK BIT_MASK(16) +#define CAL_VPORT_CTRL2_FS_RESETS_NO 0 +#define CAL_VPORT_CTRL2_FS_RESETS_YES 1 +#define CAL_VPORT_CTRL2_FSM_RESET_MASK BIT_MASK(17) +#define CAL_VPORT_CTRL2_FSM_RESET_NOEFFECT 0 +#define CAL_VPORT_CTRL2_FSM_RESET 1 +#define CAL_VPORT_CTRL2_RDY_THR_MASK GENMASK(31, 18) + +#define CAL_BYS_CTRL1_PCLK_MASK GENMASK(16, 0) +#define CAL_BYS_CTRL1_XBLK_MASK GENMASK(24, 17) +#define CAL_BYS_CTRL1_YBLK_MASK GENMASK(30, 25) +#define CAL_BYS_CTRL1_BYSINEN_MASK BIT_MASK(31) + +#define CAL_BYS_CTRL2_CPORTIN_MASK GENMASK(4, 0) +#define CAL_BYS_CTRL2_CPORTOUT_MASK GENMASK(9, 5) +#define CAL_BYS_CTRL2_DUPLICATEDDATA_MASK BIT_MASK(10) +#define CAL_BYS_CTRL2_DUPLICATEDDATA_NO 0 +#define CAL_BYS_CTRL2_DUPLICATEDDATA_YES 1 +#define CAL_BYS_CTRL2_FREERUNNING_MASK BIT_MASK(11) +#define CAL_BYS_CTRL2_FREERUNNING_NO 0 +#define CAL_BYS_CTRL2_FREERUNNING_YES 1 + +#define CAL_RD_DMA_CTRL_GO_MASK BIT_MASK(0) +#define CAL_RD_DMA_CTRL_GO_DIS 0 +#define CAL_RD_DMA_CTRL_GO_EN 1 +#define CAL_RD_DMA_CTRL_GO_IDLE 0 +#define CAL_RD_DMA_CTRL_GO_BUSY 1 +#define CAL_RD_DMA_CTRL_INIT_MASK BIT_MASK(1) +#define CAL_RD_DMA_CTRL_BW_LIMITER_MASK GENMASK(10, 2) +#define CAL_RD_DMA_CTRL_OCP_TAG_CNT_MASK GENMASK(14, 11) +#define CAL_RD_DMA_CTRL_PCLK_MASK GENMASK(31, 15) + +#define CAL_RD_DMA_PIX_ADDR_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_PIX_OFST_MASK GENMASK(31, 4) + +#define CAL_RD_DMA_XSIZE_MASK GENMASK(31, 19) + +#define CAL_RD_DMA_YSIZE_MASK GENMASK(29, 16) + +#define CAL_RD_DMA_INIT_ADDR_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_INIT_OFST_MASK GENMASK(31, 3) + +#define CAL_RD_DMA_CTRL2_CIRC_MODE_MASK GENMASK(2, 0) +#define CAL_RD_DMA_CTRL2_CIRC_MODE_DIS 0 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_ONE 1 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_FOUR 2 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTEEN 3 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_SIXTYFOUR 4 +#define CAL_RD_DMA_CTRL2_CIRC_MODE_RESERVED 5 +#define CAL_RD_DMA_CTRL2_ICM_CSTART_MASK BIT_MASK(3) +#define CAL_RD_DMA_CTRL2_PATTERN_MASK GENMASK(5, 4) +#define CAL_RD_DMA_CTRL2_PATTERN_LINEAR 0 +#define CAL_RD_DMA_CTRL2_PATTERN_YUV420 1 +#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP2 2 +#define CAL_RD_DMA_CTRL2_PATTERN_RD2SKIP4 3 +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_MASK BIT_MASK(6) +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_FREERUNNING 0 +#define CAL_RD_DMA_CTRL2_BYSOUT_LE_WAIT_WAITFORBYSOUT 1 +#define CAL_RD_DMA_CTRL2_CIRC_SIZE_MASK GENMASK(29, 16) + +#define CAL_WR_DMA_CTRL_MODE_MASK GENMASK(2, 0) +#define CAL_WR_DMA_CTRL_MODE_DIS 0 +#define CAL_WR_DMA_CTRL_MODE_SHD 1 +#define CAL_WR_DMA_CTRL_MODE_CNT 2 +#define CAL_WR_DMA_CTRL_MODE_CNT_INIT 3 +#define CAL_WR_DMA_CTRL_MODE_CONST 4 +#define CAL_WR_DMA_CTRL_MODE_RESERVED 5 +#define CAL_WR_DMA_CTRL_PATTERN_MASK GENMASK(4, 3) +#define CAL_WR_DMA_CTRL_PATTERN_LINEAR 0 +#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP2 2 +#define CAL_WR_DMA_CTRL_PATTERN_WR2SKIP4 3 +#define CAL_WR_DMA_CTRL_PATTERN_RESERVED 1 +#define CAL_WR_DMA_CTRL_ICM_PSTART_MASK BIT_MASK(5) +#define CAL_WR_DMA_CTRL_DTAG_MASK GENMASK(8, 6) +#define CAL_WR_DMA_CTRL_DTAG_ATT_HDR 0 +#define CAL_WR_DMA_CTRL_DTAG_ATT_DAT 1 +#define CAL_WR_DMA_CTRL_DTAG 2 +#define CAL_WR_DMA_CTRL_DTAG_PIX_HDR 3 +#define CAL_WR_DMA_CTRL_DTAG_PIX_DAT 4 +#define CAL_WR_DMA_CTRL_DTAG_D5 5 +#define CAL_WR_DMA_CTRL_DTAG_D6 6 +#define CAL_WR_DMA_CTRL_DTAG_D7 7 +#define CAL_WR_DMA_CTRL_CPORT_MASK GENMASK(13, 9) +#define CAL_WR_DMA_CTRL_STALL_RD_MASK BIT_MASK(14) +#define CAL_WR_DMA_CTRL_YSIZE_MASK GENMASK(31, 18) + +#define CAL_WR_DMA_ADDR_MASK GENMASK(31, 4) + +#define CAL_WR_DMA_OFST_MASK GENMASK(18, 4) +#define CAL_WR_DMA_OFST_CIRC_MODE_MASK GENMASK(23, 22) +#define CAL_WR_DMA_OFST_CIRC_MODE_ONE 1 +#define CAL_WR_DMA_OFST_CIRC_MODE_FOUR 2 +#define CAL_WR_DMA_OFST_CIRC_MODE_SIXTYFOUR 3 +#define CAL_WR_DMA_OFST_CIRC_MODE_DISABLED 0 +#define CAL_WR_DMA_OFST_CIRC_SIZE_MASK GENMASK(31, 24) + +#define CAL_WR_DMA_XSIZE_XSKIP_MASK GENMASK(15, 3) +#define CAL_WR_DMA_XSIZE_MASK GENMASK(31, 19) + +#define CAL_CSI2_PPI_CTRL_IF_EN_MASK BIT_MASK(0) +#define CAL_CSI2_PPI_CTRL_ECC_EN_MASK BIT_MASK(2) +#define CAL_CSI2_PPI_CTRL_FRAME_MASK BIT_MASK(3) +#define CAL_CSI2_PPI_CTRL_FRAME_IMMEDIATE 0 +#define CAL_CSI2_PPI_CTRL_FRAME 1 + +#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK GENMASK(2, 0) +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_5 5 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_4 4 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_3 3 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_2 2 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_1 1 +#define CAL_CSI2_COMPLEXIO_CFG_POSITION_NOT_USED 0 +#define CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK BIT_MASK(3) +#define CAL_CSI2_COMPLEXIO_CFG_POL_PLUSMINUS 0 +#define CAL_CSI2_COMPLEXIO_CFG_POL_MINUSPLUS 1 +#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POSITION_MASK GENMASK(6, 4) +#define CAL_CSI2_COMPLEXIO_CFG_DATA1_POL_MASK BIT_MASK(7) +#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POSITION_MASK GENMASK(10, 8) +#define CAL_CSI2_COMPLEXIO_CFG_DATA2_POL_MASK BIT_MASK(11) +#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POSITION_MASK GENMASK(14, 12) +#define CAL_CSI2_COMPLEXIO_CFG_DATA3_POL_MASK BIT_MASK(15) +#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POSITION_MASK GENMASK(18, 16) +#define CAL_CSI2_COMPLEXIO_CFG_DATA4_POL_MASK BIT_MASK(19) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_AUTO_MASK BIT_MASK(24) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK GENMASK(26, 25) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF 0 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ON 1 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_ULP 2 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK GENMASK(28, 27) +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF 0 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON 1 +#define CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ULP 2 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK BIT_MASK(29) +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED 1 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING 0 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK BIT_MASK(30) +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL 0 +#define CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL 1 + +#define CAL_CSI2_SHORT_PACKET_MASK GENMASK(23, 0) + +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS1_MASK BIT_MASK(0) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS2_MASK BIT_MASK(1) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS3_MASK BIT_MASK(2) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS4_MASK BIT_MASK(3) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTHS5_MASK BIT_MASK(4) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1_MASK BIT_MASK(5) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2_MASK BIT_MASK(6) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3_MASK BIT_MASK(7) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4_MASK BIT_MASK(8) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5_MASK BIT_MASK(9) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC1_MASK BIT_MASK(10) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC2_MASK BIT_MASK(11) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC3_MASK BIT_MASK(12) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC4_MASK BIT_MASK(13) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRESC5_MASK BIT_MASK(14) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL1_MASK BIT_MASK(15) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL2_MASK BIT_MASK(16) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL3_MASK BIT_MASK(17) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL4_MASK BIT_MASK(18) +#define CAL_CSI2_COMPLEXIO_IRQ_ERRCONTROL5_MASK BIT_MASK(19) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM1_MASK BIT_MASK(20) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM2_MASK BIT_MASK(21) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM3_MASK BIT_MASK(22) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM4_MASK BIT_MASK(23) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEULPM5_MASK BIT_MASK(24) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER_MASK BIT_MASK(25) +#define CAL_CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT_MASK BIT_MASK(26) +#define CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK BIT_MASK(27) +#define CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK BIT_MASK(28) +#define CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK BIT_MASK(30) + +#define CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK GENMASK(12, 0) +#define CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK BIT_MASK(13) +#define CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK BIT_MASK(14) +#define CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK BIT_MASK(15) + +#define CAL_CSI2_VC_IRQ_FS_IRQ_0_MASK BIT_MASK(0) +#define CAL_CSI2_VC_IRQ_FE_IRQ_0_MASK BIT_MASK(1) +#define CAL_CSI2_VC_IRQ_LS_IRQ_0_MASK BIT_MASK(2) +#define CAL_CSI2_VC_IRQ_LE_IRQ_0_MASK BIT_MASK(3) +#define CAL_CSI2_VC_IRQ_CS_IRQ_0_MASK BIT_MASK(4) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_0_MASK BIT_MASK(5) +#define CAL_CSI2_VC_IRQ_FS_IRQ_1_MASK BIT_MASK(8) +#define CAL_CSI2_VC_IRQ_FE_IRQ_1_MASK BIT_MASK(9) +#define CAL_CSI2_VC_IRQ_LS_IRQ_1_MASK BIT_MASK(10) +#define CAL_CSI2_VC_IRQ_LE_IRQ_1_MASK BIT_MASK(11) +#define CAL_CSI2_VC_IRQ_CS_IRQ_1_MASK BIT_MASK(12) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_1_MASK BIT_MASK(13) +#define CAL_CSI2_VC_IRQ_FS_IRQ_2_MASK BIT_MASK(16) +#define CAL_CSI2_VC_IRQ_FE_IRQ_2_MASK BIT_MASK(17) +#define CAL_CSI2_VC_IRQ_LS_IRQ_2_MASK BIT_MASK(18) +#define CAL_CSI2_VC_IRQ_LE_IRQ_2_MASK BIT_MASK(19) +#define CAL_CSI2_VC_IRQ_CS_IRQ_2_MASK BIT_MASK(20) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_2_MASK BIT_MASK(21) +#define CAL_CSI2_VC_IRQ_FS_IRQ_3_MASK BIT_MASK(24) +#define CAL_CSI2_VC_IRQ_FE_IRQ_3_MASK BIT_MASK(25) +#define CAL_CSI2_VC_IRQ_LS_IRQ_3_MASK BIT_MASK(26) +#define CAL_CSI2_VC_IRQ_LE_IRQ_3_MASK BIT_MASK(27) +#define CAL_CSI2_VC_IRQ_CS_IRQ_3_MASK BIT_MASK(28) +#define CAL_CSI2_VC_IRQ_ECC_CORRECTION0_IRQ_3_MASK BIT_MASK(29) + +#define CAL_CSI2_CTX_DT_MASK GENMASK(5, 0) +#define CAL_CSI2_CTX_VC_MASK GENMASK(7, 6) +#define CAL_CSI2_CTX_CPORT_MASK GENMASK(12, 8) +#define CAL_CSI2_CTX_ATT_MASK BIT_MASK(13) +#define CAL_CSI2_CTX_ATT_PIX 0 +#define CAL_CSI2_CTX_ATT 1 +#define CAL_CSI2_CTX_PACK_MODE_MASK BIT_MASK(14) +#define CAL_CSI2_CTX_PACK_MODE_LINE 0 +#define CAL_CSI2_CTX_PACK_MODE_FRAME 1 +#define CAL_CSI2_CTX_LINES_MASK GENMASK(29, 16) + +#define CAL_CSI2_STATUS_FRAME_MASK GENMASK(15, 0) + +#define CAL_CSI2_PHY_REG0_THS_SETTLE_MASK GENMASK(7, 0) +#define CAL_CSI2_PHY_REG0_THS_TERM_MASK GENMASK(15, 8) +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK BIT_MASK(24) +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE 1 +#define CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_ENABLE 0 + +#define CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK GENMASK(7, 0) +#define CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK GENMASK(9, 8) +#define CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK GENMASK(17, 10) +#define CAL_CSI2_PHY_REG1_TCLK_TERM_MASK GENMASK(24, 18) +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_MASK BIT_MASK(25) +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_ERROR 1 +#define CAL_CSI2_PHY_REG1_CLOCK_MISS_DETECTOR_STATUS_SUCCESS 0 +#define CAL_CSI2_PHY_REG1_RESET_DONE_STATUS_MASK GENMASK(29, 28) + +#define CAL_CSI2_PHY_REG2_CCP2_SYNC_PATTERN_MASK GENMASK(23, 0) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK GENMASK(25, 24) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK GENMASK(27, 26) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK GENMASK(29, 28) +#define CAL_CSI2_PHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK GENMASK(31, 30) + +#define CM_CAMERRX_CTRL_CSI1_CTRLCLKEN_MASK BIT_MASK(0) +#define CM_CAMERRX_CTRL_CSI1_CAMMODE_MASK GENMASK(2, 1) +#define CM_CAMERRX_CTRL_CSI1_LANEENABLE_MASK GENMASK(4, 3) +#define CM_CAMERRX_CTRL_CSI1_MODE_MASK BIT_MASK(5) +#define CM_CAMERRX_CTRL_CSI0_CTRLCLKEN_MASK BIT_MASK(10) +#define CM_CAMERRX_CTRL_CSI0_CAMMODE_MASK GENMASK(12, 11) +#define CM_CAMERRX_CTRL_CSI0_LANEENABLE_MASK GENMASK(16, 13) +#define CM_CAMERRX_CTRL_CSI0_MODE_MASK BIT_MASK(17) + +#endif diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c deleted file mode 100644 index 113c9f3c0b3e..000000000000 --- a/drivers/media/platform/timblogiw.c +++ /dev/null @@ -1,870 +0,0 @@ -/* - * timblogiw.c timberdale FPGA LogiWin Video In driver - * Copyright (c) 2009-2010 Intel Corporation - * - * 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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA LogiWin Video In - */ - -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dmaengine.h> -#include <linux/scatterlist.h> -#include <linux/interrupt.h> -#include <linux/list.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <media/videobuf-dma-contig.h> -#include <linux/platform_data/media/timb_video.h> - -#define DRIVER_NAME "timb-video" - -#define TIMBLOGIWIN_NAME "Timberdale Video-In" -#define TIMBLOGIW_VERSION_CODE 0x04 - -#define TIMBLOGIW_LINES_PER_DESC 44 -#define TIMBLOGIW_MAX_VIDEO_MEM 16 - -#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name) - - -struct timblogiw { - struct video_device video_dev; - struct v4l2_device v4l2_dev; /* mutual exclusion */ - struct mutex lock; - struct device *dev; - struct timb_video_platform_data pdata; - struct v4l2_subdev *sd_enc; /* encoder */ - bool opened; -}; - -struct timblogiw_tvnorm { - v4l2_std_id std; - u16 width; - u16 height; - u8 fps; -}; - -struct timblogiw_fh { - struct videobuf_queue vb_vidq; - struct timblogiw_tvnorm const *cur_norm; - struct list_head capture; - struct dma_chan *chan; - spinlock_t queue_lock; /* mutual exclusion */ - unsigned int frame_count; -}; - -struct timblogiw_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - struct scatterlist sg[16]; - dma_cookie_t cookie; - struct timblogiw_fh *fh; -}; - -static const struct timblogiw_tvnorm timblogiw_tvnorms[] = { - { - .std = V4L2_STD_PAL, - .width = 720, - .height = 576, - .fps = 25 - }, - { - .std = V4L2_STD_NTSC, - .width = 720, - .height = 480, - .fps = 30 - } -}; - -static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm) -{ - return norm->width * 2; -} - - -static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm) -{ - return norm->height * timblogiw_bytes_per_line(norm); -} - -static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std) -{ - int i; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - if (timblogiw_tvnorms[i].std & std) - return timblogiw_tvnorms + i; - - /* default to first element */ - return timblogiw_tvnorms; -} - -static void timblogiw_dma_cb(void *data) -{ - struct timblogiw_buffer *buf = data; - struct timblogiw_fh *fh = buf->fh; - struct videobuf_buffer *vb = &buf->vb; - - spin_lock(&fh->queue_lock); - - /* mark the transfer done */ - buf->cookie = -1; - - fh->frame_count++; - - if (vb->state != VIDEOBUF_ERROR) { - list_del(&vb->queue); - v4l2_get_timestamp(&vb->ts); - vb->field_count = fh->frame_count * 2; - vb->state = VIDEOBUF_DONE; - - wake_up(&vb->done); - } - - if (!list_empty(&fh->capture)) { - vb = list_entry(fh->capture.next, struct videobuf_buffer, - queue); - vb->state = VIDEOBUF_ACTIVE; - } - - spin_unlock(&fh->queue_lock); -} - -static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param) -{ - return chan->chan_id == (uintptr_t)filter_param; -} - -/* IOCTL functions */ - -static int timblogiw_g_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - mutex_lock(&lw->lock); - - format->fmt.pix.width = fh->cur_norm->width; - format->fmt.pix.height = fh->cur_norm->height; - format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; - format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm); - format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm); - format->fmt.pix.field = V4L2_FIELD_NONE; - - mutex_unlock(&lw->lock); - - return 0; -} - -static int timblogiw_try_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_pix_format *pix = &format->fmt.pix; - - dev_dbg(&vdev->dev, - "%s - width=%d, height=%d, pixelformat=%d, field=%d\n" - "bytes per line %d, size image: %d, colorspace: %d\n", - __func__, - pix->width, pix->height, pix->pixelformat, pix->field, - pix->bytesperline, pix->sizeimage, pix->colorspace); - - if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY) - return -EINVAL; - - return 0; -} - -static int timblogiw_s_fmt(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - struct v4l2_pix_format *pix = &format->fmt.pix; - int err; - - mutex_lock(&lw->lock); - - err = timblogiw_try_fmt(file, priv, format); - if (err) - goto out; - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dev_err(&vdev->dev, "%s queue busy\n", __func__); - err = -EBUSY; - goto out; - } - - pix->width = fh->cur_norm->width; - pix->height = fh->cur_norm->height; - -out: - mutex_unlock(&lw->lock); - return err; -} - -static int timblogiw_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1); - strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int timblogiw_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index); - - if (fmt->index != 0) - return -EINVAL; - memset(fmt, 0, sizeof(*fmt)); - fmt->index = 0; - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strncpy(fmt->description, "4:2:2, packed, YUYV", - sizeof(fmt->description)-1); - fmt->pixelformat = V4L2_PIX_FMT_UYVY; - - return 0; -} - -static int timblogiw_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct timblogiw_fh *fh = priv; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = fh->cur_norm->fps; - - return 0; -} - -static int timblogiw_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_reqbufs(&fh->vb_vidq, rb); -} - -static int timblogiw_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_querybuf(&fh->vb_vidq, b); -} - -static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_qbuf(&fh->vb_vidq, b); -} - -static int timblogiw_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); -} - -static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - *std = fh->cur_norm->std; - return 0; -} - -static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - - if (TIMBLOGIW_HAS_DECODER(lw)) - err = v4l2_subdev_call(lw->sd_enc, video, s_std, std); - - if (!err) - fh->cur_norm = timblogiw_get_norm(std); - - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_enuminput(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct video_device *vdev = video_devdata(file); - int i; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (inp->index != 0) - return -EINVAL; - - inp->index = 0; - - strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); - inp->type = V4L2_INPUT_TYPE_CAMERA; - - inp->std = 0; - for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) - inp->std |= timblogiw_tvnorms[i].std; - - return 0; -} - -static int timblogiw_g_input(struct file *file, void *priv, - unsigned int *input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - *input = 0; - - return 0; -} - -static int timblogiw_s_input(struct file *file, void *priv, unsigned int input) -{ - struct video_device *vdev = video_devdata(file); - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - if (input != 0) - return -EINVAL; - return 0; -} - -static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(&vdev->dev, "%s - No capture device\n", __func__); - return -EINVAL; - } - - fh->frame_count = 0; - return videobuf_streamon(&fh->vb_vidq); -} - -static int timblogiw_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vb_vidq); -} - -static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s entry\n", __func__); - - if (TIMBLOGIW_HAS_DECODER(lw)) - return v4l2_subdev_call(lw->sd_enc, video, querystd, std); - else { - *std = fh->cur_norm->std; - return 0; - } -} - -static int timblogiw_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = priv; - - dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__, - fsize->index, fsize->pixel_format); - - if ((fsize->index != 0) || - (fsize->pixel_format != V4L2_PIX_FMT_UYVY)) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fh->cur_norm->width; - fsize->discrete.height = fh->cur_norm->height; - - return 0; -} - -/* Video buffer functions */ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct timblogiw_fh *fh = vq->priv_data; - - *size = timblogiw_frame_size(fh->cur_norm); - - if (!*count) - *count = 32; - - while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024) - (*count)--; - - return 0; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - unsigned int data_size = timblogiw_frame_size(fh->cur_norm); - int err = 0; - - if (vb->baddr && vb->bsize < data_size) - /* User provided buffer, but it is too small */ - return -ENOMEM; - - vb->size = data_size; - vb->width = fh->cur_norm->width; - vb->height = fh->cur_norm->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int i; - unsigned int size; - unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - dma_addr_t addr; - - sg_init_table(buf->sg, ARRAY_SIZE(buf->sg)); - - err = videobuf_iolock(vq, vb, NULL); - if (err) - goto err; - - addr = videobuf_to_dma_contig(vb); - for (i = 0, size = 0; size < data_size; i++) { - sg_dma_address(buf->sg + i) = addr + size; - size += bytes_per_desc; - sg_dma_len(buf->sg + i) = (size > data_size) ? - (bytes_per_desc - (size - data_size)) : - bytes_per_desc; - } - - vb->state = VIDEOBUF_PREPARED; - buf->cookie = -1; - buf->fh = fh; - } - - return 0; - -err: - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; - return err; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - struct dma_async_tx_descriptor *desc; - int sg_elems; - int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * - timblogiw_bytes_per_line(fh->cur_norm); - - sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc; - sg_elems += - (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0; - - if (list_empty(&fh->capture)) - vb->state = VIDEOBUF_ACTIVE; - else - vb->state = VIDEOBUF_QUEUED; - - list_add_tail(&vb->queue, &fh->capture); - - spin_unlock_irq(&fh->queue_lock); - - desc = dmaengine_prep_slave_sg(fh->chan, - buf->sg, sg_elems, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); - if (!desc) { - spin_lock_irq(&fh->queue_lock); - list_del_init(&vb->queue); - vb->state = VIDEOBUF_PREPARED; - return; - } - - desc->callback_param = buf; - desc->callback = timblogiw_dma_cb; - - buf->cookie = desc->tx_submit(desc); - - spin_lock_irq(&fh->queue_lock); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct timblogiw_fh *fh = vq->priv_data; - struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, - vb); - - videobuf_waiton(vq, vb, 0, 0); - if (buf->cookie >= 0) - dma_sync_wait(fh->chan, buf->cookie); - - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops timblogiw_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* Device Operations functions */ - -static int timblogiw_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh; - v4l2_std_id std; - dma_cap_mask_t mask; - int err = 0; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - mutex_lock(&lw->lock); - if (lw->opened) { - err = -EBUSY; - goto out; - } - - if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) { - struct i2c_adapter *adapt; - - /* find the video decoder */ - adapt = i2c_get_adapter(lw->pdata.i2c_adapter); - if (!adapt) { - dev_err(&vdev->dev, "No I2C bus #%d\n", - lw->pdata.i2c_adapter); - err = -ENODEV; - goto out; - } - - /* now find the encoder */ - lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt, - lw->pdata.encoder.info, NULL); - - i2c_put_adapter(adapt); - - if (!lw->sd_enc) { - dev_err(&vdev->dev, "Failed to get encoder: %s\n", - lw->pdata.encoder.module_name); - err = -ENODEV; - goto out; - } - } - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) { - err = -ENOMEM; - goto out; - } - - fh->cur_norm = timblogiw_tvnorms; - timblogiw_querystd(file, fh, &std); - fh->cur_norm = timblogiw_get_norm(std); - - INIT_LIST_HEAD(&fh->capture); - spin_lock_init(&fh->queue_lock); - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - - /* find the DMA channel */ - fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn, - (void *)(uintptr_t)lw->pdata.dma_channel); - if (!fh->chan) { - dev_err(&vdev->dev, "Failed to get DMA channel\n"); - kfree(fh); - err = -ENODEV; - goto out; - } - - file->private_data = fh; - videobuf_queue_dma_contig_init(&fh->vb_vidq, - &timblogiw_video_qops, lw->dev, &fh->queue_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct timblogiw_buffer), fh, NULL); - - lw->opened = true; -out: - mutex_unlock(&lw->lock); - - return err; -} - -static int timblogiw_close(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw *lw = video_get_drvdata(vdev); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - - dma_release_channel(fh->chan); - - kfree(fh); - - mutex_lock(&lw->lock); - lw->opened = false; - mutex_unlock(&lw->lock); - return 0; -} - -static ssize_t timblogiw_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static unsigned int timblogiw_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_poll_stream(file, &fh->vb_vidq, wait); -} - -static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - struct timblogiw_fh *fh = file->private_data; - - dev_dbg(&vdev->dev, "%s: entry\n", __func__); - - return videobuf_mmap_mapper(&fh->vb_vidq, vma); -} - -/* Platform device functions */ - -static struct v4l2_ioctl_ops timblogiw_ioctl_ops = { - .vidioc_querycap = timblogiw_querycap, - .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt, - .vidioc_g_fmt_vid_cap = timblogiw_g_fmt, - .vidioc_try_fmt_vid_cap = timblogiw_try_fmt, - .vidioc_s_fmt_vid_cap = timblogiw_s_fmt, - .vidioc_g_parm = timblogiw_g_parm, - .vidioc_reqbufs = timblogiw_reqbufs, - .vidioc_querybuf = timblogiw_querybuf, - .vidioc_qbuf = timblogiw_qbuf, - .vidioc_dqbuf = timblogiw_dqbuf, - .vidioc_g_std = timblogiw_g_std, - .vidioc_s_std = timblogiw_s_std, - .vidioc_enum_input = timblogiw_enuminput, - .vidioc_g_input = timblogiw_g_input, - .vidioc_s_input = timblogiw_s_input, - .vidioc_streamon = timblogiw_streamon, - .vidioc_streamoff = timblogiw_streamoff, - .vidioc_querystd = timblogiw_querystd, - .vidioc_enum_framesizes = timblogiw_enum_framesizes, -}; - -static struct v4l2_file_operations timblogiw_fops = { - .owner = THIS_MODULE, - .open = timblogiw_open, - .release = timblogiw_close, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = timblogiw_mmap, - .read = timblogiw_read, - .poll = timblogiw_poll, -}; - -static struct video_device timblogiw_template = { - .name = TIMBLOGIWIN_NAME, - .fops = &timblogiw_fops, - .ioctl_ops = &timblogiw_ioctl_ops, - .release = video_device_release_empty, - .minor = -1, - .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC -}; - -static int timblogiw_probe(struct platform_device *pdev) -{ - int err; - struct timblogiw *lw = NULL; - struct timb_video_platform_data *pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "No platform data\n"); - err = -EINVAL; - goto err; - } - - if (!pdata->encoder.module_name) - dev_info(&pdev->dev, "Running without decoder\n"); - - lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL); - if (!lw) { - err = -ENOMEM; - goto err; - } - - if (pdev->dev.parent) - lw->dev = pdev->dev.parent; - else - lw->dev = &pdev->dev; - - memcpy(&lw->pdata, pdata, sizeof(lw->pdata)); - - mutex_init(&lw->lock); - - lw->video_dev = timblogiw_template; - - strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name)); - err = v4l2_device_register(NULL, &lw->v4l2_dev); - if (err) - goto err; - - lw->video_dev.v4l2_dev = &lw->v4l2_dev; - - platform_set_drvdata(pdev, lw); - video_set_drvdata(&lw->video_dev, lw); - - err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0); - if (err) { - dev_err(&pdev->dev, "Error reg video: %d\n", err); - goto err_request; - } - - return 0; - -err_request: - v4l2_device_unregister(&lw->v4l2_dev); -err: - dev_err(&pdev->dev, "Failed to register: %d\n", err); - - return err; -} - -static int timblogiw_remove(struct platform_device *pdev) -{ - struct timblogiw *lw = platform_get_drvdata(pdev); - - video_unregister_device(&lw->video_dev); - - v4l2_device_unregister(&lw->v4l2_dev); - - return 0; -} - -static struct platform_driver timblogiw_platform_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = timblogiw_probe, - .remove = timblogiw_remove, -}; - -module_platform_driver(timblogiw_platform_driver); - -MODULE_DESCRIPTION(TIMBLOGIWIN_NAME); -MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 418113c99801..c4b5fab83666 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1074,7 +1074,7 @@ static int __init vim2m_init(void) if (ret) platform_device_unregister(&vim2m_pdev); - return 0; + return ret; } module_init(vim2m_init); diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c index e15eef6a94e5..bdc380b14e0c 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -360,7 +360,7 @@ void vivid_fb_release_buffers(struct vivid_dev *dev) /* Release pseudo palette */ kfree(dev->fb_info.pseudo_palette); - kfree((void *)dev->video_vbase); + kfree(dev->video_vbase); } /* Initialize the specified card */ diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h index 9baed6a10334..93fbaee69675 100644 --- a/drivers/media/platform/vivid/vivid-tpg.h +++ b/drivers/media/platform/vivid/vivid-tpg.h @@ -418,6 +418,8 @@ static inline void tpg_s_bytesperline(struct tpg_data *tpg, unsigned plane, unsi tpg->bytesperline[p] = plane_w / tpg->hdownsampling[p]; } + if (tpg_g_interleaved(tpg)) + tpg->bytesperline[1] = tpg->bytesperline[0]; } diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 6a93f928dfde..95b3ac2ea7ef 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,4 +1,5 @@ -vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o +vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o +vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 989e96f7e360..910d6b8e8b50 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -26,6 +26,9 @@ struct clk; struct device; +struct vsp1_dl; +struct vsp1_drm; +struct vsp1_entity; struct vsp1_platform_data; struct vsp1_bru; struct vsp1_hsit; @@ -42,17 +45,21 @@ struct vsp1_uds; #define VSP1_HAS_LIF (1 << 0) #define VSP1_HAS_LUT (1 << 1) #define VSP1_HAS_SRU (1 << 2) +#define VSP1_HAS_BRU (1 << 3) -struct vsp1_platform_data { +struct vsp1_device_info { + u32 version; unsigned int features; unsigned int rpf_count; unsigned int uds_count; unsigned int wpf_count; + unsigned int num_bru_inputs; + bool uapi; }; struct vsp1_device { struct device *dev; - struct vsp1_platform_data pdata; + const struct vsp1_device_info *info; void __iomem *mmio; struct clk *clock; @@ -71,14 +78,22 @@ struct vsp1_device { struct vsp1_rwpf *wpf[VSP1_MAX_WPF]; struct list_head entities; + struct list_head videos; struct v4l2_device v4l2_dev; struct media_device media_dev; + struct media_entity_operations media_ops; + + struct vsp1_drm *drm; + + bool use_dl; }; int vsp1_device_get(struct vsp1_device *vsp1); void vsp1_device_put(struct vsp1_device *vsp1); +int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index); + static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg) { return ioread32(vsp1->mmio + reg); @@ -89,4 +104,14 @@ static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data) iowrite32(data, vsp1->mmio + reg); } +#include "vsp1_dl.h" + +static inline void vsp1_mod_write(struct vsp1_entity *e, u32 reg, u32 data) +{ + if (e->vsp1->use_dl) + vsp1_dl_add(e, reg, data); + else + vsp1_write(e->vsp1, reg, data); +} + #endif /* __VSP1_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 7dd763311c0f..cb0dbc15ddad 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -19,6 +19,7 @@ #include "vsp1.h" #include "vsp1_bru.h" #include "vsp1_rwpf.h" +#include "vsp1_video.h" #define BRU_MIN_SIZE 1U #define BRU_MAX_SIZE 8190U @@ -27,14 +28,9 @@ * Device Access */ -static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg) -{ - return vsp1_read(bru->entity.vsp1, reg); -} - static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) { - vsp1_write(bru->entity.vsp1, reg, data); + vsp1_mod_write(&bru->entity, reg, data); } /* ----------------------------------------------------------------------------- @@ -83,7 +79,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) return 0; - format = &bru->entity.formats[BRU_PAD_SOURCE]; + format = &bru->entity.formats[bru->entity.source_pad]; /* The hardware is extremely flexible but we have no userspace API to * expose all the parameters, nor is it clear whether we would have use @@ -94,7 +90,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) /* Disable dithering and enable color data normalization unless the * format at the pipeline output is premultiplied. */ - flags = pipe->output ? pipe->output->video.format.flags : 0; + flags = pipe->output ? pipe->output->format.flags : 0; vsp1_bru_write(bru, VI6_BRU_INCTRL, flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); @@ -113,7 +109,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) VI6_BRU_ROP_CROP(VI6_ROP_NOP) | VI6_BRU_ROP_AROP(VI6_ROP_NOP)); - for (i = 0; i < 4; ++i) { + for (i = 0; i < bru->entity.source_pad; ++i) { bool premultiplied = false; u32 ctrl = 0; @@ -125,7 +121,7 @@ static int bru_s_stream(struct v4l2_subdev *subdev, int enable) if (bru->inputs[i].rpf) { ctrl |= VI6_BRU_CTRL_RBC; - premultiplied = bru->inputs[i].rpf->video.format.flags + premultiplied = bru->inputs[i].rpf->format.flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; } else { ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) @@ -295,7 +291,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con *format = fmt->format; /* Reset the compose rectangle */ - if (fmt->pad != BRU_PAD_SOURCE) { + if (fmt->pad != bru->entity.source_pad) { struct v4l2_rect *compose; compose = bru_get_compose(bru, cfg, fmt->pad, fmt->which); @@ -309,7 +305,7 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_con if (fmt->pad == BRU_PAD_SINK(0)) { unsigned int i; - for (i = 0; i <= BRU_PAD_SOURCE; ++i) { + for (i = 0; i <= bru->entity.source_pad; ++i) { format = vsp1_entity_get_pad_format(&bru->entity, cfg, i, fmt->which); format->code = fmt->format.code; @@ -325,7 +321,7 @@ static int bru_get_selection(struct v4l2_subdev *subdev, { struct vsp1_bru *bru = to_bru(subdev); - if (sel->pad == BRU_PAD_SOURCE) + if (sel->pad == bru->entity.source_pad) return -EINVAL; switch (sel->target) { @@ -353,7 +349,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; - if (sel->pad == BRU_PAD_SOURCE) + if (sel->pad == bru->entity.source_pad) return -EINVAL; if (sel->target != V4L2_SEL_TGT_COMPOSE) @@ -362,8 +358,8 @@ static int bru_set_selection(struct v4l2_subdev *subdev, /* The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, cfg, BRU_PAD_SOURCE, - sel->which); + format = vsp1_entity_get_pad_format(&bru->entity, cfg, + bru->entity.source_pad, sel->which); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -419,7 +415,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) bru->entity.type = VSP1_ENTITY_BRU; - ret = vsp1_entity_init(vsp1, &bru->entity, 5); + ret = vsp1_entity_init(vsp1, &bru->entity, + vsp1->info->num_bru_inputs + 1); if (ret < 0) return ERR_PTR(ret); @@ -427,7 +424,7 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) subdev = &bru->entity.subdev; v4l2_subdev_init(subdev, &bru_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s bru", dev_name(vsp1->dev)); diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h index 16b1c6554911..dbac9686ea69 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -23,7 +23,6 @@ struct vsp1_device; struct vsp1_rwpf; #define BRU_PAD_SINK(n) (n) -#define BRU_PAD_SOURCE 4 struct vsp1_bru { struct vsp1_entity entity; @@ -33,7 +32,7 @@ struct vsp1_bru { struct { struct vsp1_rwpf *rpf; struct v4l2_rect compose; - } inputs[4]; + } inputs[VSP1_MAX_RPF]; }; static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c new file mode 100644 index 000000000000..7dc27ac6bd02 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -0,0 +1,305 @@ +/* + * vsp1_dl.h -- R-Car VSP1 Display List + * + * Copyright (C) 2015 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/gfp.h> +#include <linux/slab.h> + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_pipe.h" + +/* + * Global resources + * + * - Display-related interrupts (can be used for vblank evasion ?) + * - Display-list enable + * - Header-less for WPF0 + * - DL swap + */ + +#define VSP1_DL_BODY_SIZE (2 * 4 * 256) +#define VSP1_DL_NUM_LISTS 3 + +struct vsp1_dl_entry { + u32 addr; + u32 data; +} __attribute__((__packed__)); + +struct vsp1_dl_list { + size_t size; + int reg_count; + + bool in_use; + + struct vsp1_dl_entry *body; + dma_addr_t dma; +}; + +/** + * struct vsp1_dl - Display List manager + * @vsp1: the VSP1 device + * @lock: protects the active, queued and pending lists + * @lists.all: array of all allocate display lists + * @lists.active: list currently being processed (loaded) by hardware + * @lists.queued: list queued to the hardware (written to the DL registers) + * @lists.pending: list waiting to be queued to the hardware + * @lists.write: list being written to by software + */ +struct vsp1_dl { + struct vsp1_device *vsp1; + + spinlock_t lock; + + size_t size; + dma_addr_t dma; + void *mem; + + struct { + struct vsp1_dl_list all[VSP1_DL_NUM_LISTS]; + + struct vsp1_dl_list *active; + struct vsp1_dl_list *queued; + struct vsp1_dl_list *pending; + struct vsp1_dl_list *write; + } lists; +}; + +/* ----------------------------------------------------------------------------- + * Display List Transaction Management + */ + +static void vsp1_dl_free_list(struct vsp1_dl_list *list) +{ + if (!list) + return; + + list->in_use = false; +} + +void vsp1_dl_reset(struct vsp1_dl *dl) +{ + unsigned int i; + + dl->lists.active = NULL; + dl->lists.queued = NULL; + dl->lists.pending = NULL; + dl->lists.write = NULL; + + for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) + dl->lists.all[i].in_use = false; +} + +void vsp1_dl_begin(struct vsp1_dl *dl) +{ + struct vsp1_dl_list *list = NULL; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&dl->lock, flags); + + for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) { + if (!dl->lists.all[i].in_use) { + list = &dl->lists.all[i]; + break; + } + } + + if (!list) { + list = dl->lists.pending; + dl->lists.pending = NULL; + } + + spin_unlock_irqrestore(&dl->lock, flags); + + dl->lists.write = list; + + list->in_use = true; + list->reg_count = 0; +} + +void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data) +{ + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&e->subdev.entity); + struct vsp1_dl *dl = pipe->dl; + struct vsp1_dl_list *list = dl->lists.write; + + list->body[list->reg_count].addr = reg; + list->body[list->reg_count].data = data; + list->reg_count++; +} + +void vsp1_dl_commit(struct vsp1_dl *dl) +{ + struct vsp1_device *vsp1 = dl->vsp1; + struct vsp1_dl_list *list; + unsigned long flags; + bool update; + + list = dl->lists.write; + dl->lists.write = NULL; + + spin_lock_irqsave(&dl->lock, flags); + + /* Once the UPD bit has been set the hardware can start processing the + * display list at any time and we can't touch the address and size + * registers. In that case mark the update as pending, it will be + * queued up to the hardware by the frame end interrupt handler. + */ + update = !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD); + if (update) { + vsp1_dl_free_list(dl->lists.pending); + dl->lists.pending = list; + goto done; + } + + /* Program the hardware with the display list body address and size. + * The UPD bit will be cleared by the device when the display list is + * processed. + */ + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma); + vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | + (list->reg_count * 8)); + + vsp1_dl_free_list(dl->lists.queued); + dl->lists.queued = list; + +done: + spin_unlock_irqrestore(&dl->lock, flags); +} + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +void vsp1_dl_irq_display_start(struct vsp1_dl *dl) +{ + spin_lock(&dl->lock); + + /* The display start interrupt signals the end of the display list + * processing by the device. The active display list, if any, won't be + * accessed anymore and can be reused. + */ + if (dl->lists.active) { + vsp1_dl_free_list(dl->lists.active); + dl->lists.active = NULL; + } + + spin_unlock(&dl->lock); +} + +void vsp1_dl_irq_frame_end(struct vsp1_dl *dl) +{ + struct vsp1_device *vsp1 = dl->vsp1; + + spin_lock(&dl->lock); + + /* The UPD bit set indicates that the commit operation raced with the + * interrupt and occurred after the frame end event and UPD clear but + * before interrupt processing. The hardware hasn't taken the update + * into account yet, we'll thus skip one frame and retry. + */ + if (vsp1_read(vsp1, VI6_DL_BODY_SIZE) & VI6_DL_BODY_SIZE_UPD) + goto done; + + /* The device starts processing the queued display list right after the + * frame end interrupt. The display list thus becomes active. + */ + if (dl->lists.queued) { + WARN_ON(dl->lists.active); + dl->lists.active = dl->lists.queued; + dl->lists.queued = NULL; + } + + /* Now that the UPD bit has been cleared we can queue the next display + * list to the hardware if one has been prepared. + */ + if (dl->lists.pending) { + struct vsp1_dl_list *list = dl->lists.pending; + + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), list->dma); + vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | + (list->reg_count * 8)); + + dl->lists.queued = list; + dl->lists.pending = NULL; + } + +done: + spin_unlock(&dl->lock); +} + +/* ----------------------------------------------------------------------------- + * Hardware Setup + */ + +void vsp1_dl_setup(struct vsp1_device *vsp1) +{ + u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) + | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 + | VI6_DL_CTRL_DLE; + + /* The DRM pipeline operates with header-less display lists in + * Continuous Frame Mode. + */ + if (vsp1->drm) + ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; + + vsp1_write(vsp1, VI6_DL_CTRL, ctrl); + vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); +} + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1) +{ + struct vsp1_dl *dl; + unsigned int i; + + dl = kzalloc(sizeof(*dl), GFP_KERNEL); + if (!dl) + return NULL; + + spin_lock_init(&dl->lock); + + dl->vsp1 = vsp1; + dl->size = VSP1_DL_BODY_SIZE * ARRAY_SIZE(dl->lists.all); + + dl->mem = dma_alloc_writecombine(vsp1->dev, dl->size, &dl->dma, + GFP_KERNEL); + if (!dl->mem) { + kfree(dl); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(dl->lists.all); ++i) { + struct vsp1_dl_list *list = &dl->lists.all[i]; + + list->size = VSP1_DL_BODY_SIZE; + list->reg_count = 0; + list->in_use = false; + list->dma = dl->dma + VSP1_DL_BODY_SIZE * i; + list->body = dl->mem + VSP1_DL_BODY_SIZE * i; + } + + return dl; +} + +void vsp1_dl_destroy(struct vsp1_dl *dl) +{ + dma_free_writecombine(dl->vsp1->dev, dl->size, dl->mem, dl->dma); + kfree(dl); +} diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h new file mode 100644 index 000000000000..448c4250e54c --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -0,0 +1,42 @@ +/* + * vsp1_dl.h -- R-Car VSP1 Display List + * + * Copyright (C) 2015 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_DL_H__ +#define __VSP1_DL_H__ + +#include "vsp1_entity.h" + +struct vsp1_device; +struct vsp1_dl; + +struct vsp1_dl *vsp1_dl_create(struct vsp1_device *vsp1); +void vsp1_dl_destroy(struct vsp1_dl *dl); + +void vsp1_dl_setup(struct vsp1_device *vsp1); + +void vsp1_dl_reset(struct vsp1_dl *dl); +void vsp1_dl_begin(struct vsp1_dl *dl); +void vsp1_dl_add(struct vsp1_entity *e, u32 reg, u32 data); +void vsp1_dl_commit(struct vsp1_dl *dl); + +void vsp1_dl_irq_display_start(struct vsp1_dl *dl); +void vsp1_dl_irq_frame_end(struct vsp1_dl *dl); + +static inline void vsp1_dl_mod_write(struct vsp1_entity *e, u32 reg, u32 data) +{ + if (e->vsp1->use_dl) + vsp1_dl_add(e, reg, data); + else + vsp1_write(e->vsp1, reg, data); +} + +#endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c new file mode 100644 index 000000000000..021fe5778cd1 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -0,0 +1,597 @@ +/* + * vsp1_drm.c -- R-Car VSP1 DRM API + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/vsp1.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_bru.h" +#include "vsp1_dl.h" +#include "vsp1_drm.h" +#include "vsp1_lif.h" +#include "vsp1_pipe.h" +#include "vsp1_rwpf.h" + +/* ----------------------------------------------------------------------------- + * Runtime Handling + */ + +static void vsp1_drm_pipeline_frame_end(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->num_inputs) + vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +/* ----------------------------------------------------------------------------- + * DU Driver API + */ + +int vsp1_du_init(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + if (!vsp1) + return -EPROBE_DEFER; + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_init); + +/** + * vsp1_du_setup_lif - Setup the output part of the VSP pipeline + * @dev: the VSP device + * @width: output frame width in pixels + * @height: output frame height in pixels + * + * Configure the output part of VSP DRM pipeline for the given frame @width and + * @height. This sets up formats on the BRU source pad, the WPF0 sink and source + * pads, and the LIF sink pad. + * + * As the media bus code on the BRU source pad is conditioned by the + * configuration of the BRU sink 0 pad, we also set up the formats on all BRU + * sinks, even if the configuration will be overwritten later by + * vsp1_du_setup_rpf(). This ensures that the BRU configuration is set to a well + * defined state. + * + * Return 0 on success or a negative error code on failure. + */ +int vsp1_du_setup_lif(struct device *dev, unsigned int width, + unsigned int height) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_pipeline *pipe = &vsp1->drm->pipe; + struct vsp1_bru *bru = vsp1->bru; + struct v4l2_subdev_format format; + unsigned int i; + int ret; + + dev_dbg(vsp1->dev, "%s: configuring LIF with format %ux%u\n", + __func__, width, height); + + if (width == 0 || height == 0) { + /* Zero width or height means the CRTC is being disabled, stop + * the pipeline and turn the light off. + */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); + + media_entity_pipeline_stop(&pipe->output->entity.subdev.entity); + + for (i = 0; i < bru->entity.source_pad; ++i) { + bru->inputs[i].rpf = NULL; + pipe->inputs[i] = NULL; + } + + pipe->num_inputs = 0; + + vsp1_device_put(vsp1); + + dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__); + + return 0; + } + + vsp1_dl_reset(vsp1->drm->dl); + + /* Configure the format at the BRU sinks and propagate it through the + * pipeline. + */ + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + for (i = 0; i < bru->entity.source_pad; ++i) { + format.pad = i; + + format.format.width = width; + format.format.height = height; + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&bru->entity.subdev, pad, + set_fmt, NULL, &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, i); + } + + format.pad = bru->entity.source_pad; + format.format.width = width; + format.format.height = height; + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&bru->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, i); + + format.pad = RWPF_PAD_SINK; + ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF0 sink\n", + __func__, format.format.width, format.format.height, + format.format.code); + + format.pad = RWPF_PAD_SOURCE; + ret = v4l2_subdev_call(&vsp1->wpf[0]->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF0 source\n", + __func__, format.format.width, format.format.height, + format.format.code); + + format.pad = LIF_PAD_SINK; + ret = v4l2_subdev_call(&vsp1->lif->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF sink\n", + __func__, format.format.width, format.format.height, + format.format.code); + + /* Verify that the format at the output of the pipeline matches the + * requested frame size and media bus code. + */ + if (format.format.width != width || format.format.height != height || + format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) { + dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__); + return -EPIPE; + } + + /* Mark the pipeline as streaming and enable the VSP1. This will store + * the pipeline pointer in all entities, which the s_stream handlers + * will need. We don't start the entities themselves right at this point + * as there's no plane configured yet, so we can't start processing + * buffers. + */ + ret = vsp1_device_get(vsp1); + if (ret < 0) + return ret; + + ret = media_entity_pipeline_start(&pipe->output->entity.subdev.entity, + &pipe->pipe); + if (ret < 0) { + dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__); + vsp1_device_put(vsp1); + return ret; + } + + dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__); + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); + +/** + * vsp1_du_atomic_begin - Prepare for an atomic update + * @dev: the VSP device + */ +void vsp1_du_atomic_begin(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_pipeline *pipe = &vsp1->drm->pipe; + unsigned long flags; + + spin_lock_irqsave(&pipe->irqlock, flags); + + vsp1->drm->num_inputs = pipe->num_inputs; + + spin_unlock_irqrestore(&pipe->irqlock, flags); + + /* Prepare the display list. */ + vsp1_dl_begin(vsp1->drm->dl); +} +EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); + +/** + * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline + * @dev: the VSP device + * @rpf_index: index of the RPF to setup (0-based) + * @pixelformat: V4L2 pixel format for the RPF memory input + * @pitch: number of bytes per line in the image stored in memory + * @mem: DMA addresses of the memory buffers (one per plane) + * @src: the source crop rectangle for the RPF + * @dst: the destination compose rectangle for the BRU input + * + * Configure the VSP to perform composition of the image referenced by @mem + * through RPF @rpf_index, using the @src crop rectangle and the @dst + * composition rectangle. The Z-order is fixed with RPF 0 at the bottom. + * + * Image format as stored in memory is expressed as a V4L2 @pixelformat value. + * As a special case, setting the pixel format to 0 will disable the RPF. The + * @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the + * function on a disabled RPF is allowed. + * + * The memory pitch is configurable to allow for padding at end of lines, or + * simple for images that extend beyond the crop rectangle boundaries. The + * @pitch value is expressed in bytes and applies to all planes for multiplanar + * formats. + * + * The source memory buffer is referenced by the DMA address of its planes in + * the @mem array. Up to two planes are supported. The second plane DMA address + * is ignored for formats using a single plane. + * + * This function isn't reentrant, the caller needs to serialize calls. + * + * TODO: Implement Z-order control by decoupling the RPF index from the BRU + * input index. + * + * Return 0 on success or a negative error code on failure. + */ +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, + u32 pixelformat, unsigned int pitch, + dma_addr_t mem[2], const struct v4l2_rect *src, + const struct v4l2_rect *dst) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_pipeline *pipe = &vsp1->drm->pipe; + const struct vsp1_format_info *fmtinfo; + struct v4l2_subdev_selection sel; + struct v4l2_subdev_format format; + struct vsp1_rwpf_memory memory; + struct vsp1_rwpf *rpf; + unsigned long flags; + int ret; + + if (rpf_index >= vsp1->info->rpf_count) + return -EINVAL; + + rpf = vsp1->rpf[rpf_index]; + + if (pixelformat == 0) { + dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__, + rpf_index); + + spin_lock_irqsave(&pipe->irqlock, flags); + + if (pipe->inputs[rpf_index]) { + /* Remove the RPF from the pipeline if it was previously + * enabled. + */ + vsp1->bru->inputs[rpf_index].rpf = NULL; + pipe->inputs[rpf_index] = NULL; + + pipe->num_inputs--; + } + + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return 0; + } + + dev_dbg(vsp1->dev, + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad }\n", + __func__, rpf_index, + src->left, src->top, src->width, src->height, + dst->left, dst->top, dst->width, dst->height, + pixelformat, pitch, &mem[0], &mem[1]); + + /* Set the stride at the RPF input. */ + fmtinfo = vsp1_get_format_info(pixelformat); + if (!fmtinfo) { + dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", + pixelformat); + return -EINVAL; + } + + rpf->fmtinfo = fmtinfo; + rpf->format.num_planes = fmtinfo->planes; + rpf->format.plane_fmt[0].bytesperline = pitch; + rpf->format.plane_fmt[1].bytesperline = pitch; + + /* Configure the format on the RPF sink pad and propagate it up to the + * BRU sink pad. + */ + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = src->width + src->left; + format.format.height = src->height + src->top; + format.format.code = fmtinfo->mbus; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set format %ux%u (%x) on RPF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + memset(&sel, 0, sizeof(sel)); + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = RWPF_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = *src; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, + &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + rpf->entity.index); + + /* RPF source, hardcode the format to ARGB8888 to turn on format + * conversion if needed. + */ + format.pad = RWPF_PAD_SOURCE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: got format %ux%u (%x) on RPF%u source\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + /* BRU sink, propagate the format from the RPF source. */ + format.pad = rpf->entity.index; + + ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on BRU pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, format.pad); + + sel.pad = rpf->entity.index; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.r = *dst; + + ret = v4l2_subdev_call(&vsp1->bru->entity.subdev, pad, set_selection, + NULL, &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set selection (%u,%u)/%ux%u on BRU pad %u\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + sel.pad); + + /* Store the compose rectangle coordinates in the RPF. */ + rpf->location.left = dst->left; + rpf->location.top = dst->top; + + /* Set the memory buffer address. */ + memory.num_planes = fmtinfo->planes; + memory.addr[0] = mem[0]; + memory.addr[1] = mem[1]; + + rpf->ops->set_memory(rpf, &memory); + + spin_lock_irqsave(&pipe->irqlock, flags); + + /* If the RPF was previously stopped set the BRU input to the RPF and + * store the RPF in the pipeline inputs array. + */ + if (!pipe->inputs[rpf->entity.index]) { + vsp1->bru->inputs[rpf_index].rpf = rpf; + pipe->inputs[rpf->entity.index] = rpf; + pipe->num_inputs++; + } + + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); + +/** + * vsp1_du_atomic_flush - Commit an atomic update + * @dev: the VSP device + */ +void vsp1_du_atomic_flush(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + struct vsp1_pipeline *pipe = &vsp1->drm->pipe; + struct vsp1_entity *entity; + unsigned long flags; + bool stop = false; + int ret; + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + /* Disconnect unused RPFs from the pipeline. */ + if (entity->type == VSP1_ENTITY_RPF) { + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + if (!pipe->inputs[rpf->entity.index]) { + vsp1_mod_write(entity, entity->route->reg, + VI6_DPR_NODE_UNUSED); + continue; + } + } + + vsp1_entity_route_setup(entity); + + ret = v4l2_subdev_call(&entity->subdev, video, + s_stream, 1); + if (ret < 0) { + dev_err(vsp1->dev, + "DRM pipeline start failure on entity %s\n", + entity->subdev.name); + return; + } + } + + vsp1_dl_commit(vsp1->drm->dl); + + spin_lock_irqsave(&pipe->irqlock, flags); + + /* Start or stop the pipeline if needed. */ + if (!vsp1->drm->num_inputs && pipe->num_inputs) { + vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); + vsp1_write(vsp1, VI6_DISP_IRQ_ENB, VI6_DISP_IRQ_ENB_DSTE); + vsp1_pipeline_run(pipe); + } else if (vsp1->drm->num_inputs && !pipe->num_inputs) { + stop = true; + } + + spin_unlock_irqrestore(&pipe->irqlock, flags); + + if (stop) { + vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0); + vsp1_pipeline_stop(pipe); + } +} +EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +int vsp1_drm_create_links(struct vsp1_device *vsp1) +{ + const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; + unsigned int i; + int ret; + + /* VSPD instances require a BRU to perform composition and a LIF to + * output to the DU. + */ + if (!vsp1->bru || !vsp1->lif) + return -ENXIO; + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rpf = vsp1->rpf[i]; + + ret = media_create_pad_link(&rpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &vsp1->bru->entity.subdev.entity, + i, flags); + if (ret < 0) + return ret; + + rpf->entity.sink = &vsp1->bru->entity.subdev.entity; + rpf->entity.sink_pad = i; + } + + ret = media_create_pad_link(&vsp1->bru->entity.subdev.entity, + vsp1->bru->entity.source_pad, + &vsp1->wpf[0]->entity.subdev.entity, + RWPF_PAD_SINK, flags); + if (ret < 0) + return ret; + + vsp1->bru->entity.sink = &vsp1->wpf[0]->entity.subdev.entity; + vsp1->bru->entity.sink_pad = RWPF_PAD_SINK; + + ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, + RWPF_PAD_SOURCE, + &vsp1->lif->entity.subdev.entity, + LIF_PAD_SINK, flags); + if (ret < 0) + return ret; + + return 0; +} + +int vsp1_drm_init(struct vsp1_device *vsp1) +{ + struct vsp1_pipeline *pipe; + unsigned int i; + + vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL); + if (!vsp1->drm) + return -ENOMEM; + + vsp1->drm->dl = vsp1_dl_create(vsp1); + if (!vsp1->drm->dl) + return -ENOMEM; + + pipe = &vsp1->drm->pipe; + + vsp1_pipeline_init(pipe); + pipe->frame_end = vsp1_drm_pipeline_frame_end; + + /* The DRM pipeline is static, add entities manually. */ + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *input = vsp1->rpf[i]; + + list_add_tail(&input->entity.list_pipe, &pipe->entities); + } + + list_add_tail(&vsp1->bru->entity.list_pipe, &pipe->entities); + list_add_tail(&vsp1->wpf[0]->entity.list_pipe, &pipe->entities); + list_add_tail(&vsp1->lif->entity.list_pipe, &pipe->entities); + + pipe->bru = &vsp1->bru->entity; + pipe->lif = &vsp1->lif->entity; + pipe->output = vsp1->wpf[0]; + + pipe->dl = vsp1->drm->dl; + + return 0; +} + +void vsp1_drm_cleanup(struct vsp1_device *vsp1) +{ + vsp1_dl_destroy(vsp1->drm->dl); +} diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h new file mode 100644 index 000000000000..f68056838319 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -0,0 +1,49 @@ +/* + * vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_DRM_H__ +#define __VSP1_DRM_H__ + +#include "vsp1_pipe.h" + +struct vsp1_dl; + +/** + * vsp1_drm - State for the API exposed to the DRM driver + * @dl: display list for DRM pipeline operation + * @pipe: the VSP1 pipeline used for display + * @num_inputs: number of active pipeline inputs at the beginning of an update + * @update: the pipeline configuration has been updated + */ +struct vsp1_drm { + struct vsp1_dl *dl; + struct vsp1_pipeline pipe; + unsigned int num_inputs; + bool update; +}; + +int vsp1_drm_init(struct vsp1_device *vsp1); +void vsp1_drm_cleanup(struct vsp1_device *vsp1); +int vsp1_drm_create_links(struct vsp1_device *vsp1); + +int vsp1_du_init(struct device *dev); +int vsp1_du_setup_lif(struct device *dev, unsigned int width, + unsigned int height); +void vsp1_du_atomic_begin(struct device *dev); +int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, + u32 pixelformat, unsigned int pitch, + dma_addr_t mem[2], const struct v4l2_rect *src, + const struct v4l2_rect *dst); +void vsp1_du_atomic_flush(struct device *dev); + + +#endif /* __VSP1_DRM_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 533bc796391e..25750a0e4631 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -17,17 +17,23 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/videodev2.h> +#include <media/v4l2-subdev.h> + #include "vsp1.h" #include "vsp1_bru.h" +#include "vsp1_dl.h" +#include "vsp1_drm.h" #include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_lut.h" #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" +#include "vsp1_video.h" /* ----------------------------------------------------------------------------- * Interrupt Handling @@ -39,11 +45,11 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) struct vsp1_device *vsp1 = data; irqreturn_t ret = IRQ_NONE; unsigned int i; + u32 status; - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; - u32 status; if (wpf == NULL) continue; @@ -58,6 +64,21 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) } } + status = vsp1_read(vsp1, VI6_DISP_IRQ_STA); + vsp1_write(vsp1, VI6_DISP_IRQ_STA, ~status & VI6_DISP_IRQ_STA_DST); + + if (status & VI6_DISP_IRQ_STA_DST) { + struct vsp1_rwpf *wpf = vsp1->wpf[0]; + struct vsp1_pipeline *pipe; + + if (wpf) { + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + vsp1_pipeline_display_start(pipe); + } + + ret = IRQ_HANDLED; + } + return ret; } @@ -66,7 +87,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) */ /* - * vsp1_create_links - Create links from all sources to the given sink + * vsp1_create_sink_links - Create links from all sources to the given sink * * This function creates media links from all valid sources to the given sink * pad. Links that would be invalid according to the VSP1 hardware capabilities @@ -75,7 +96,8 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) * - from a UDS to a UDS (UDS entities can't be chained) * - from an entity to itself (no loops are allowed) */ -static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) +static int vsp1_create_sink_links(struct vsp1_device *vsp1, + struct vsp1_entity *sink) { struct media_entity *entity = &sink->subdev.entity; struct vsp1_entity *source; @@ -115,19 +137,86 @@ static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) return 0; } -static void vsp1_destroy_entities(struct vsp1_device *vsp1) +static int vsp1_uapi_create_links(struct vsp1_device *vsp1) { struct vsp1_entity *entity; - struct vsp1_entity *next; + unsigned int i; + int ret; + + list_for_each_entry(entity, &vsp1->entities, list_dev) { + if (entity->type == VSP1_ENTITY_LIF || + entity->type == VSP1_ENTITY_RPF) + continue; + + ret = vsp1_create_sink_links(vsp1, entity); + if (ret < 0) + return ret; + } + + if (vsp1->info->features & VSP1_HAS_LIF) { + ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity, + RWPF_PAD_SOURCE, + &vsp1->lif->entity.subdev.entity, + LIF_PAD_SINK, 0); + if (ret < 0) + return ret; + } + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rpf = vsp1->rpf[i]; - list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) { + ret = media_create_pad_link(&rpf->video->video.entity, 0, + &rpf->entity.subdev.entity, + RWPF_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + return ret; + } + + for (i = 0; i < vsp1->info->wpf_count; ++i) { + /* Connect the video device to the WPF. All connections are + * immutable except for the WPF0 source link if a LIF is + * present. + */ + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + unsigned int flags = MEDIA_LNK_FL_ENABLED; + + if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0) + flags |= MEDIA_LNK_FL_IMMUTABLE; + + ret = media_create_pad_link(&wpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &wpf->video->video.entity, 0, + flags); + if (ret < 0) + return ret; + } + + return 0; +} + +static void vsp1_destroy_entities(struct vsp1_device *vsp1) +{ + struct vsp1_entity *entity, *_entity; + struct vsp1_video *video, *_video; + + list_for_each_entry_safe(entity, _entity, &vsp1->entities, list_dev) { list_del(&entity->list_dev); vsp1_entity_destroy(entity); } + list_for_each_entry_safe(video, _video, &vsp1->videos, list) { + list_del(&video->list); + vsp1_video_cleanup(video); + } + v4l2_device_unregister(&vsp1->v4l2_dev); media_device_unregister(&vsp1->media_dev); media_device_cleanup(&vsp1->media_dev); + + if (!vsp1->info->uapi) + vsp1_drm_cleanup(vsp1); } static int vsp1_create_entities(struct vsp1_device *vsp1) @@ -144,6 +233,14 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) dev_name(mdev->dev)); media_device_init(mdev); + vsp1->media_ops.link_setup = vsp1_entity_link_setup; + /* Don't perform link validation when the userspace API is disabled as + * the pipeline is configured internally by the driver in that case, and + * its configuration can thus be trusted. + */ + if (vsp1->info->uapi) + vsp1->media_ops.link_validate = v4l2_subdev_link_validate; + vdev->mdev = mdev; ret = v4l2_device_register(vsp1->dev, vdev); if (ret < 0) { @@ -153,13 +250,15 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Instantiate all the entities. */ - vsp1->bru = vsp1_bru_create(vsp1); - if (IS_ERR(vsp1->bru)) { - ret = PTR_ERR(vsp1->bru); - goto done; - } + if (vsp1->info->features & VSP1_HAS_BRU) { + vsp1->bru = vsp1_bru_create(vsp1); + if (IS_ERR(vsp1->bru)) { + ret = PTR_ERR(vsp1->bru); + goto done; + } - list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); + list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); + } vsp1->hsi = vsp1_hsit_create(vsp1, true); if (IS_ERR(vsp1->hsi)) { @@ -177,7 +276,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->pdata.features & VSP1_HAS_LIF) { + if (vsp1->info->features & VSP1_HAS_LIF) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { ret = PTR_ERR(vsp1->lif); @@ -187,7 +286,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); } - if (vsp1->pdata.features & VSP1_HAS_LUT) { + if (vsp1->info->features & VSP1_HAS_LUT) { vsp1->lut = vsp1_lut_create(vsp1); if (IS_ERR(vsp1->lut)) { ret = PTR_ERR(vsp1->lut); @@ -197,7 +296,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata.rpf_count; ++i) { + for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rpf; rpf = vsp1_rpf_create(vsp1, i); @@ -208,9 +307,20 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) vsp1->rpf[i] = rpf; list_add_tail(&rpf->entity.list_dev, &vsp1->entities); + + if (vsp1->info->uapi) { + struct vsp1_video *video = vsp1_video_create(vsp1, rpf); + + if (IS_ERR(video)) { + ret = PTR_ERR(video); + goto done; + } + + list_add_tail(&video->list, &vsp1->videos); + } } - if (vsp1->pdata.features & VSP1_HAS_SRU) { + if (vsp1->info->features & VSP1_HAS_SRU) { vsp1->sru = vsp1_sru_create(vsp1); if (IS_ERR(vsp1->sru)) { ret = PTR_ERR(vsp1->sru); @@ -220,7 +330,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata.uds_count; ++i) { + for (i = 0; i < vsp1->info->uds_count; ++i) { struct vsp1_uds *uds; uds = vsp1_uds_create(vsp1, i); @@ -233,7 +343,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { + for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf; wpf = vsp1_wpf_create(vsp1, i); @@ -244,6 +354,18 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) vsp1->wpf[i] = wpf; list_add_tail(&wpf->entity.list_dev, &vsp1->entities); + + if (vsp1->info->uapi) { + struct vsp1_video *video = vsp1_video_create(vsp1, wpf); + + if (IS_ERR(video)) { + ret = PTR_ERR(video); + goto done; + } + + list_add_tail(&video->list, &vsp1->videos); + wpf->entity.sink = &video->video.entity; + } } /* Register all subdevs. */ @@ -255,34 +377,23 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Create links. */ - list_for_each_entry(entity, &vsp1->entities, list_dev) { - if (entity->type == VSP1_ENTITY_WPF) { - ret = vsp1_wpf_create_links(vsp1, entity); - if (ret < 0) - goto done; - } else if (entity->type == VSP1_ENTITY_RPF) { - ret = vsp1_rpf_create_links(vsp1, entity); - if (ret < 0) - goto done; - } - - if (entity->type != VSP1_ENTITY_LIF && - entity->type != VSP1_ENTITY_RPF) { - ret = vsp1_create_links(vsp1, entity); - if (ret < 0) - goto done; - } - } + if (vsp1->info->uapi) + ret = vsp1_uapi_create_links(vsp1); + else + ret = vsp1_drm_create_links(vsp1); + if (ret < 0) + goto done; - if (vsp1->pdata.features & VSP1_HAS_LIF) { - ret = media_create_pad_link( - &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, - &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); - if (ret < 0) - return ret; + /* Register subdev nodes if the userspace API is enabled or initialize + * the DRM pipeline otherwise. + */ + if (vsp1->info->uapi) { + vsp1->use_dl = false; + ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); + } else { + vsp1->use_dl = true; + ret = vsp1_drm_init(vsp1); } - - ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); if (ret < 0) goto done; @@ -295,42 +406,51 @@ done: return ret; } -static int vsp1_device_init(struct vsp1_device *vsp1) +int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index) { - unsigned int i; + unsigned int timeout; u32 status; - /* Reset any channel that might be running. */ status = vsp1_read(vsp1, VI6_STATUS); + if (!(status & VI6_STATUS_SYS_ACT(index))) + return 0; - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { - unsigned int timeout; + vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(index)); + for (timeout = 10; timeout > 0; --timeout) { + status = vsp1_read(vsp1, VI6_STATUS); + if (!(status & VI6_STATUS_SYS_ACT(index))) + break; - if (!(status & VI6_STATUS_SYS_ACT(i))) - continue; + usleep_range(1000, 2000); + } - vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i)); - for (timeout = 10; timeout > 0; --timeout) { - status = vsp1_read(vsp1, VI6_STATUS); - if (!(status & VI6_STATUS_SYS_ACT(i))) - break; + if (!timeout) { + dev_err(vsp1->dev, "failed to reset wpf.%u\n", index); + return -ETIMEDOUT; + } - usleep_range(1000, 2000); - } + return 0; +} - if (!timeout) { - dev_err(vsp1->dev, "failed to reset wpf.%u\n", i); - return -ETIMEDOUT; - } +static int vsp1_device_init(struct vsp1_device *vsp1) +{ + unsigned int i; + int ret; + + /* Reset any channel that might be running. */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + ret = vsp1_reset_wpf(vsp1, i); + if (ret < 0) + return ret; } vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); - for (i = 0; i < vsp1->pdata.rpf_count; ++i) + for (i = 0; i < vsp1->info->rpf_count; ++i) vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < vsp1->pdata.uds_count; ++i) + for (i = 0; i < vsp1->info->uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -345,6 +465,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); + if (vsp1->use_dl) + vsp1_dl_setup(vsp1); + return 0; } @@ -444,48 +567,76 @@ static const struct dev_pm_ops vsp1_pm_ops = { * Platform Driver */ -static int vsp1_parse_dt(struct vsp1_device *vsp1) -{ - struct device_node *np = vsp1->dev->of_node; - struct vsp1_platform_data *pdata = &vsp1->pdata; - - if (of_property_read_bool(np, "renesas,has-lif")) - pdata->features |= VSP1_HAS_LIF; - if (of_property_read_bool(np, "renesas,has-lut")) - pdata->features |= VSP1_HAS_LUT; - if (of_property_read_bool(np, "renesas,has-sru")) - pdata->features |= VSP1_HAS_SRU; - - of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); - of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); - of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); - - if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) { - dev_err(vsp1->dev, "invalid number of RPF (%u)\n", - pdata->rpf_count); - return -EINVAL; - } - - if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) { - dev_err(vsp1->dev, "invalid number of UDS (%u)\n", - pdata->uds_count); - return -EINVAL; - } - - if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) { - dev_err(vsp1->dev, "invalid number of WPF (%u)\n", - pdata->wpf_count); - return -EINVAL; - } - - return 0; -} +static const struct vsp1_device_info vsp1_device_infos[] = { + { + .version = VI6_IP_VERSION_MODEL_VSPS_H2, + .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .rpf_count = 5, + .uds_count = 3, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPR_H2, + .features = VSP1_HAS_BRU | VSP1_HAS_SRU, + .rpf_count = 5, + .uds_count = 1, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, + .rpf_count = 4, + .uds_count = 1, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPS_M2, + .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU, + .rpf_count = 5, + .uds_count = 3, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, + .features = VSP1_HAS_LUT | VSP1_HAS_SRU, + .rpf_count = 1, + .uds_count = 1, + .wpf_count = 1, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, + .features = VSP1_HAS_BRU, + .rpf_count = 5, + .wpf_count = 1, + .num_bru_inputs = 5, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, + .features = VSP1_HAS_BRU | VSP1_HAS_LUT, + .rpf_count = 5, + .wpf_count = 1, + .num_bru_inputs = 5, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, + .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, + .rpf_count = 5, + .wpf_count = 2, + .num_bru_inputs = 5, + }, +}; static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; struct resource *irq; struct resource *io; + unsigned int i; + u32 version; int ret; vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); @@ -495,10 +646,7 @@ static int vsp1_probe(struct platform_device *pdev) vsp1->dev = &pdev->dev; mutex_init(&vsp1->lock); INIT_LIST_HEAD(&vsp1->entities); - - ret = vsp1_parse_dt(vsp1); - if (ret < 0) - return ret; + INIT_LIST_HEAD(&vsp1->videos); /* I/O, IRQ and clock resources */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -525,6 +673,29 @@ static int vsp1_probe(struct platform_device *pdev) return ret; } + /* Configure device parameters based on the version register. */ + ret = clk_prepare_enable(vsp1->clock); + if (ret < 0) + return ret; + + version = vsp1_read(vsp1, VI6_IP_VERSION); + clk_disable_unprepare(vsp1->clock); + + for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { + if ((version & VI6_IP_VERSION_MODEL_MASK) == + vsp1_device_infos[i].version) { + vsp1->info = &vsp1_device_infos[i]; + break; + } + } + + if (!vsp1->info) { + dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); + return -ENXIO; + } + + dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); + /* Instanciate entities */ ret = vsp1_create_entities(vsp1); if (ret < 0) { @@ -548,6 +719,7 @@ static int vsp1_remove(struct platform_device *pdev) static const struct of_device_id vsp1_of_match[] = { { .compatible = "renesas,vsp1" }, + { .compatible = "renesas,vsp2" }, { }, }; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index d7308530952f..20a78fbd3691 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -20,7 +20,6 @@ #include "vsp1.h" #include "vsp1_entity.h" -#include "vsp1_video.h" bool vsp1_entity_is_streaming(struct vsp1_entity *entity) { @@ -46,7 +45,7 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) if (!streaming) return 0; - if (!entity->subdev.ctrl_handler) + if (!entity->vsp1->info->uapi || !entity->subdev.ctrl_handler) return 0; ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler); @@ -59,6 +58,18 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming) return ret; } +void vsp1_entity_route_setup(struct vsp1_entity *source) +{ + struct vsp1_entity *sink; + + if (source->route->reg == 0) + return; + + sink = container_of(source->sink, struct vsp1_entity, subdev.entity); + vsp1_mod_write(source, source->route->reg, + sink->route->inputs[source->sink_pad]); +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -120,9 +131,9 @@ const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { * Media Operations */ -static int vsp1_entity_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) +int vsp1_entity_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) { struct vsp1_entity *source; @@ -147,11 +158,6 @@ static int vsp1_entity_link_setup(struct media_entity *entity, return 0; } -const struct media_entity_operations vsp1_media_ops = { - .link_setup = vsp1_entity_link_setup, - .link_validate = v4l2_subdev_link_validate, -}; - /* ----------------------------------------------------------------------------- * Initialization */ @@ -159,7 +165,8 @@ const struct media_entity_operations vsp1_media_ops = { static const struct vsp1_route vsp1_routes[] = { { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), - VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } }, + VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), + VI6_DPR_NODE_BRU_IN(4) } }, { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, @@ -225,8 +232,6 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity) { - if (entity->video) - vsp1_video_cleanup(entity->video); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); media_entity_cleanup(&entity->subdev.entity); diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 8867a5787c28..83570dfde8ec 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ #include <media/v4l2-subdev.h> struct vsp1_device; -struct vsp1_video; enum vsp1_entity_type { VSP1_ENTITY_BRU, @@ -33,6 +32,8 @@ enum vsp1_entity_type { VSP1_ENTITY_WPF, }; +#define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */ + /* * struct vsp1_route - Entity routing configuration * @type: Entity type this routing entry is associated with @@ -49,7 +50,7 @@ struct vsp1_route { enum vsp1_entity_type type; unsigned int index; unsigned int reg; - unsigned int inputs[4]; + unsigned int inputs[VSP1_ENTITY_MAX_INPUTS]; }; struct vsp1_entity { @@ -71,8 +72,6 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_mbus_framefmt *formats; - struct vsp1_video *video; - spinlock_t lock; /* Protects the streaming field */ bool streaming; }; @@ -87,7 +86,10 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, void vsp1_entity_destroy(struct vsp1_entity *entity); extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; -extern const struct media_entity_operations vsp1_media_ops; + +int vsp1_entity_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags); struct v4l2_mbus_framefmt * vsp1_entity_get_pad_format(struct vsp1_entity *entity, @@ -99,4 +101,6 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, bool vsp1_entity_is_streaming(struct vsp1_entity *entity); int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming); +void vsp1_entity_route_setup(struct vsp1_entity *source); + #endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 8ffb817ae525..c1087cff31a0 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -203,7 +203,7 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) subdev = &hsit->entity.subdev; v4l2_subdev_init(subdev, &hsit_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s %s", dev_name(vsp1->dev), inverse ? "hsi" : "hst"); diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 39fa5ef20fbb..433853ce8dbf 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -26,14 +26,9 @@ * Device Access */ -static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg) -{ - return vsp1_read(lif->entity.vsp1, reg); -} - static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) { - vsp1_write(lif->entity.vsp1, reg, data); + vsp1_mod_write(&lif->entity, reg, data); } /* ----------------------------------------------------------------------------- @@ -49,7 +44,7 @@ static int lif_s_stream(struct v4l2_subdev *subdev, int enable) unsigned int lbth = 200; if (!enable) { - vsp1_lif_write(lif, VI6_LIF_CTRL, 0); + vsp1_write(lif->entity.vsp1, VI6_LIF_CTRL, 0); return 0; } @@ -228,7 +223,7 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) subdev = &lif->entity.subdev; v4l2_subdev_init(subdev, &lif_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s lif", dev_name(vsp1->dev)); diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index 656ec272a414..4b89095e7b5f 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -27,11 +27,6 @@ * Device Access */ -static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg) -{ - return vsp1_read(lut->entity.vsp1, reg); -} - static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) { vsp1_write(lut->entity.vsp1, reg, data); @@ -242,7 +237,7 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) subdev = &lut->entity.subdev; v4l2_subdev_init(subdev, &lut_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s lut", dev_name(vsp1->dev)); diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c new file mode 100644 index 000000000000..6659f06b1643 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -0,0 +1,426 @@ +/* + * vsp1_pipe.c -- R-Car VSP1 Pipeline + * + * Copyright (C) 2013-2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/wait.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_bru.h" +#include "vsp1_dl.h" +#include "vsp1_entity.h" +#include "vsp1_pipe.h" +#include "vsp1_rwpf.h" +#include "vsp1_uds.h" + +/* ----------------------------------------------------------------------------- + * Helper Functions + */ + +static const struct vsp1_format_info vsp1_video_formats[] = { + { V4L2_PIX_FMT_RGB332, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 8, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB444, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB555, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_BGR24, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_RGB24, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ABGR32, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XBGR32, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB32, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB32, MEDIA_BUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, false, false, 2, 1, false }, + { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, false, true, 2, 1, false }, + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, true, false, 2, 1, false }, + { V4L2_PIX_FMT_YVYU, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, true, true, 2, 1, false }, + { V4L2_PIX_FMT_NV12M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, false, 2, 2, false }, + { V4L2_PIX_FMT_NV21M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, true, 2, 2, false }, + { V4L2_PIX_FMT_NV16M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, false, 2, 1, false }, + { V4L2_PIX_FMT_NV61M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, true, 2, 1, false }, + { V4L2_PIX_FMT_YUV420M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, false, 2, 2, false }, + { V4L2_PIX_FMT_YVU420M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, true, 2, 2, false }, + { V4L2_PIX_FMT_YUV422M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, false, 2, 1, false }, + { V4L2_PIX_FMT_YVU422M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, true, 2, 1, false }, + { V4L2_PIX_FMT_YUV444M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_YVU444M, MEDIA_BUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, true, 1, 1, false }, +}; + +/* + * vsp1_get_format_info - Retrieve format information for a 4CC + * @fourcc: the format 4CC + * + * Return a pointer to the format information structure corresponding to the + * given V4L2 format 4CC, or NULL if no corresponding format can be found. + */ +const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { + const struct vsp1_format_info *info = &vsp1_video_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Management + */ + +void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) +{ + unsigned int i; + + if (pipe->bru) { + struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); + + for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) + bru->inputs[i].rpf = NULL; + } + + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) + pipe->inputs[i] = NULL; + + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; + pipe->buffers_ready = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->bru = NULL; + pipe->lif = NULL; + pipe->uds = NULL; +} + +void vsp1_pipeline_init(struct vsp1_pipeline *pipe) +{ + mutex_init(&pipe->lock); + spin_lock_init(&pipe->irqlock); + init_waitqueue_head(&pipe->wq); + + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; +} + +void vsp1_pipeline_run(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + + if (pipe->state == VSP1_PIPELINE_STOPPED) { + vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), + VI6_CMD_STRCMD); + pipe->state = VSP1_PIPELINE_RUNNING; + } + + pipe->buffers_ready = 0; +} + +bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + bool stopped; + + spin_lock_irqsave(&pipe->irqlock, flags); + stopped = pipe->state == VSP1_PIPELINE_STOPPED; + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return stopped; +} + +int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) +{ + struct vsp1_entity *entity; + unsigned long flags; + int ret; + + if (pipe->dl) { + /* When using display lists in continuous frame mode the only + * way to stop the pipeline is to reset the hardware. + */ + ret = vsp1_reset_wpf(pipe->output->entity.vsp1, + pipe->output->entity.index); + if (ret == 0) { + spin_lock_irqsave(&pipe->irqlock, flags); + pipe->state = VSP1_PIPELINE_STOPPED; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + } else { + /* Otherwise just request a stop and wait. */ + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + ret = ret == 0 ? -ETIMEDOUT : 0; + } + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->route && entity->route->reg) + vsp1_write(entity->vsp1, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + v4l2_subdev_call(&entity->subdev, video, s_stream, 0); + } + + return ret; +} + +bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) +{ + unsigned int mask; + + mask = ((1 << pipe->num_inputs) - 1) << 1; + if (!pipe->lif) + mask |= 1 << 0; + + return pipe->buffers_ready == mask; +} + +void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe) +{ + if (pipe->dl) + vsp1_dl_irq_display_start(pipe->dl); +} + +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) +{ + enum vsp1_pipeline_state state; + unsigned long flags; + + if (pipe == NULL) + return; + + if (pipe->dl) + vsp1_dl_irq_frame_end(pipe->dl); + + /* Signal frame end to the pipeline handler. */ + pipe->frame_end(pipe); + + spin_lock_irqsave(&pipe->irqlock, flags); + + state = pipe->state; + + /* When using display lists in continuous frame mode the pipeline is + * automatically restarted by the hardware. + */ + if (!pipe->dl) + pipe->state = VSP1_PIPELINE_STOPPED; + + /* If a stop has been requested, mark the pipeline as stopped and + * return. + */ + if (state == VSP1_PIPELINE_STOPPING) { + wake_up(&pipe->wq); + goto done; + } + + /* Restart the pipeline if ready. */ + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + +done: + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +/* + * Propagate the alpha value through the pipeline. + * + * As the UDS has restricted scaling capabilities when the alpha component needs + * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha + * value. The UDS then outputs a fixed alpha value which needs to be programmed + * from the input RPF alpha. + */ +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha) +{ + struct vsp1_entity *entity; + struct media_pad *pad; + + pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); + + while (pad) { + if (!is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* The BRU background color has a fixed alpha value set to 255, + * the output alpha value is thus always equal to 255. + */ + if (entity->type == VSP1_ENTITY_BRU) + alpha = 255; + + if (entity->type == VSP1_ENTITY_UDS) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + + vsp1_uds_set_alpha(uds, alpha); + break; + } + + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } +} + +void vsp1_pipelines_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and + * then to wait for the stop to complete. + */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_pipelines_resume(struct vsp1_device *vsp1) +{ + unsigned int i; + + /* Resume pipeline all running pipelines. */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + if (pipe == NULL) + continue; + + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + } +} diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h new file mode 100644 index 000000000000..b2f3a8a896c9 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -0,0 +1,134 @@ +/* + * vsp1_pipe.h -- R-Car VSP1 Pipeline + * + * Copyright (C) 2013-2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_PIPE_H__ +#define __VSP1_PIPE_H__ + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <media/media-entity.h> + +struct vsp1_dl; +struct vsp1_rwpf; + +/* + * struct vsp1_format_info - VSP1 video format description + * @mbus: media bus format code + * @fourcc: V4L2 pixel format FCC identifier + * @planes: number of planes + * @bpp: bits per pixel + * @hwfmt: VSP1 hardware format + * @swap_yc: the Y and C components are swapped (Y comes before C) + * @swap_uv: the U and V components are swapped (V comes before U) + * @hsub: horizontal subsampling factor + * @vsub: vertical subsampling factor + * @alpha: has an alpha channel + */ +struct vsp1_format_info { + u32 fourcc; + unsigned int mbus; + unsigned int hwfmt; + unsigned int swap; + unsigned int planes; + unsigned int bpp[3]; + bool swap_yc; + bool swap_uv; + unsigned int hsub; + unsigned int vsub; + bool alpha; +}; + +enum vsp1_pipeline_state { + VSP1_PIPELINE_STOPPED, + VSP1_PIPELINE_RUNNING, + VSP1_PIPELINE_STOPPING, +}; + +/* + * struct vsp1_pipeline - A VSP1 hardware pipeline + * @pipe: the media pipeline + * @irqlock: protects the pipeline state + * @state: current state + * @wq: work queue to wait for state change completion + * @frame_end: frame end interrupt handler + * @lock: protects the pipeline use count and stream count + * @use_count: number of video nodes using the pipeline + * @stream_count: number of streaming video nodes + * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available + * @num_inputs: number of RPFs + * @inputs: array of RPFs in the pipeline (indexed by RPF index) + * @output: WPF at the output of the pipeline + * @bru: BRU entity, if present + * @lif: LIF entity, if present + * @uds: UDS entity, if present + * @uds_input: entity at the input of the UDS, if the UDS is present + * @entities: list of entities in the pipeline + * @dl: display list associated with the pipeline + */ +struct vsp1_pipeline { + struct media_pipeline pipe; + + spinlock_t irqlock; + enum vsp1_pipeline_state state; + wait_queue_head_t wq; + + void (*frame_end)(struct vsp1_pipeline *pipe); + + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + unsigned int buffers_ready; + + unsigned int num_inputs; + struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; + struct vsp1_rwpf *output; + struct vsp1_entity *bru; + struct vsp1_entity *lif; + struct vsp1_entity *uds; + struct vsp1_entity *uds_input; + + struct list_head entities; + + struct vsp1_dl *dl; +}; + +static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) +{ + if (likely(e->pipe)) + return container_of(e->pipe, struct vsp1_pipeline, pipe); + else + return NULL; +} + +void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); +void vsp1_pipeline_init(struct vsp1_pipeline *pipe); + +void vsp1_pipeline_run(struct vsp1_pipeline *pipe); +bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); +int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); +bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); + +void vsp1_pipeline_display_start(struct vsp1_pipeline *pipe); +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); + +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha); + +void vsp1_pipelines_suspend(struct vsp1_device *vsp1); +void vsp1_pipelines_resume(struct vsp1_device *vsp1); + +const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc); + +#endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 25b48738b147..069216f0eb44 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -46,7 +46,7 @@ #define VI6_DISP_IRQ_ENB_LNEE(n) (1 << (n)) #define VI6_DISP_IRQ_STA 0x007c -#define VI6_DISP_IRQ_STA_DSE (1 << 8) +#define VI6_DISP_IRQ_STA_DST (1 << 8) #define VI6_DISP_IRQ_STA_MAE (1 << 5) #define VI6_DISP_IRQ_STA_LNE(n) (1 << (n)) @@ -322,7 +322,7 @@ #define VI6_DPR_NODE_SRU 16 #define VI6_DPR_NODE_UDS(n) (17 + (n)) #define VI6_DPR_NODE_LUT 22 -#define VI6_DPR_NODE_BRU_IN(n) (23 + (n)) +#define VI6_DPR_NODE_BRU_IN(n) (((n) <= 3) ? 23 + (n) : 49) #define VI6_DPR_NODE_BRU_OUT 27 #define VI6_DPR_NODE_CLU 29 #define VI6_DPR_NODE_HST 30 @@ -504,12 +504,12 @@ #define VI6_BRU_VIRRPF_COL_BCB_MASK (0xff << 0) #define VI6_BRU_VIRRPF_COL_BCB_SHIFT 0 -#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8) +#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8 + ((n) <= 3 ? 0 : 4)) #define VI6_BRU_CTRL_RBC (1 << 31) -#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) ((n) << 20) +#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 20) #define VI6_BRU_CTRL_DSTSEL_VRPF (4 << 20) #define VI6_BRU_CTRL_DSTSEL_MASK (7 << 20) -#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) ((n) << 16) +#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 16) #define VI6_BRU_CTRL_SRCSEL_VRPF (4 << 16) #define VI6_BRU_CTRL_SRCSEL_MASK (7 << 16) #define VI6_BRU_CTRL_CROP(rop) ((rop) << 4) @@ -517,7 +517,7 @@ #define VI6_BRU_CTRL_AROP(rop) ((rop) << 0) #define VI6_BRU_CTRL_AROP_MASK (0xf << 0) -#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8) +#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8 + ((n) <= 3 ? 0 : 4)) #define VI6_BRU_BLD_CBES (1 << 31) #define VI6_BRU_BLD_CCMDX_DST_A (0 << 28) #define VI6_BRU_BLD_CCMDX_255_DST_A (1 << 28) @@ -551,7 +551,7 @@ #define VI6_BRU_BLD_COEFY_SHIFT 0 #define VI6_BRU_ROP 0x2c30 -#define VI6_BRU_ROP_DSTSEL_BRUIN(n) ((n) << 20) +#define VI6_BRU_ROP_DSTSEL_BRUIN(n) (((n) <= 3 ? (n) : (n)+1) << 20) #define VI6_BRU_ROP_DSTSEL_VRPF (4 << 20) #define VI6_BRU_ROP_DSTSEL_MASK (7 << 20) #define VI6_BRU_ROP_CROP(rop) ((rop) << 4) @@ -625,6 +625,24 @@ #define VI6_SECURITY_CTRL1 0x3d04 /* ----------------------------------------------------------------------------- + * IP Version Registers + */ + +#define VI6_IP_VERSION 0x3f00 +#define VI6_IP_VERSION_MODEL_MASK (0xff << 8) +#define VI6_IP_VERSION_MODEL_VSPS_H2 (0x09 << 8) +#define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8) +#define VI6_IP_VERSION_MODEL_VSPD_GEN2 (0x0b << 8) +#define VI6_IP_VERSION_MODEL_VSPS_M2 (0x0c << 8) +#define VI6_IP_VERSION_MODEL_VSPI_GEN3 (0x14 << 8) +#define VI6_IP_VERSION_MODEL_VSPBD_GEN3 (0x15 << 8) +#define VI6_IP_VERSION_MODEL_VSPBC_GEN3 (0x16 << 8) +#define VI6_IP_VERSION_MODEL_VSPD_GEN3 (0x17 << 8) +#define VI6_IP_VERSION_SOC_MASK (0xff << 0) +#define VI6_IP_VERSION_SOC_H (0x01 << 0) +#define VI6_IP_VERSION_SOC_M (0x02 << 0) + +/* ----------------------------------------------------------------------------- * RPF CLUT Registers */ diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 924538223d3e..5bc1d1574a43 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -26,16 +26,10 @@ * Device Access */ -static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg) -{ - return vsp1_read(rpf->entity.vsp1, - reg + rpf->entity.index * VI6_RPF_OFFSET); -} - static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) { - vsp1_write(rpf->entity.vsp1, - reg + rpf->entity.index * VI6_RPF_OFFSET, data); + vsp1_mod_write(&rpf->entity, reg + rpf->entity.index * VI6_RPF_OFFSET, + data); } /* ----------------------------------------------------------------------------- @@ -74,9 +68,11 @@ static const struct v4l2_ctrl_ops rpf_ctrl_ops = { static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) { + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity); struct vsp1_rwpf *rpf = to_rwpf(subdev); - const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo; - const struct v4l2_pix_format_mplane *format = &rpf->video.format; + struct vsp1_device *vsp1 = rpf->entity.vsp1; + const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; + const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_rect *crop = &rpf->crop; u32 pstride; u32 infmt; @@ -154,6 +150,15 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); + + if (vsp1->info->uapi) + mutex_lock(rpf->ctrls.lock); + vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, + rpf->alpha->cur.val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, rpf->alpha->cur.val); + if (vsp1->info->uapi) + mutex_unlock(rpf->ctrls.lock); + vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); @@ -186,30 +191,28 @@ static struct v4l2_subdev_ops rpf_ops = { * Video Device Operations */ -static void rpf_vdev_queue(struct vsp1_video *video, - struct vsp1_video_buffer *buf) +static void rpf_set_memory(struct vsp1_rwpf *rpf, struct vsp1_rwpf_memory *mem) { - struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); unsigned int i; for (i = 0; i < 3; ++i) - rpf->buf_addr[i] = buf->addr[i]; + rpf->buf_addr[i] = mem->addr[i]; if (!vsp1_entity_is_streaming(&rpf->entity)) return; vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, - buf->addr[0] + rpf->offsets[0]); - if (buf->buf.vb2_buf.num_planes > 1) + mem->addr[0] + rpf->offsets[0]); + if (mem->num_planes > 1) vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, - buf->addr[1] + rpf->offsets[1]); - if (buf->buf.vb2_buf.num_planes > 2) + mem->addr[1] + rpf->offsets[1]); + if (mem->num_planes > 2) vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, - buf->addr[2] + rpf->offsets[1]); + mem->addr[2] + rpf->offsets[1]); } -static const struct vsp1_video_operations rpf_vdev_ops = { - .queue = rpf_vdev_queue, +static const struct vsp1_rwpf_operations rpf_vdev_ops = { + .set_memory = rpf_set_memory, }; /* ----------------------------------------------------------------------------- @@ -219,7 +222,6 @@ static const struct vsp1_video_operations rpf_vdev_ops = { struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) { struct v4l2_subdev *subdev; - struct vsp1_video *video; struct vsp1_rwpf *rpf; int ret; @@ -227,6 +229,8 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) if (rpf == NULL) return ERR_PTR(-ENOMEM); + rpf->ops = &rpf_vdev_ops; + rpf->max_width = RPF_MAX_WIDTH; rpf->max_height = RPF_MAX_HEIGHT; @@ -241,7 +245,7 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) subdev = &rpf->entity.subdev; v4l2_subdev_init(subdev, &rpf_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", dev_name(vsp1->dev), index); @@ -252,8 +256,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) /* Initialize the control handler. */ v4l2_ctrl_handler_init(&rpf->ctrls, 1); - v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, - 0, 255, 1, 255); + rpf->alpha = v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); rpf->entity.subdev.ctrl_handler = &rpf->ctrls; @@ -264,42 +269,9 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; } - /* Initialize the video device. */ - video = &rpf->video; - - video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - video->vsp1 = vsp1; - video->ops = &rpf_vdev_ops; - - ret = vsp1_video_init(video, &rpf->entity); - if (ret < 0) - goto error; - - rpf->entity.video = video; - return rpf; error: vsp1_entity_destroy(&rpf->entity); return ERR_PTR(ret); } - -/* - * vsp1_rpf_create_links() - RPF pads links creation - * @vsp1: Pointer to VSP1 device - * @entity: Pointer to VSP1 entity - * - * return negative error code or zero on success - */ -int vsp1_rpf_create_links(struct vsp1_device *vsp1, - struct vsp1_entity *entity) -{ - struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); - - /* Connect the video device to the RPF. */ - return media_create_pad_link(&rpf->video.video.entity, 0, - &rpf->entity.subdev.entity, - RWPF_PAD_SINK, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); -} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 731d36e5258d..8e8235682ada 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -19,19 +19,39 @@ #include "vsp1.h" #include "vsp1_entity.h" -#include "vsp1_video.h" #define RWPF_PAD_SINK 0 #define RWPF_PAD_SOURCE 1 +struct v4l2_ctrl; +struct vsp1_rwpf; +struct vsp1_video; + +struct vsp1_rwpf_memory { + unsigned int num_planes; + dma_addr_t addr[3]; + unsigned int length[3]; +}; + +struct vsp1_rwpf_operations { + void (*set_memory)(struct vsp1_rwpf *rwpf, + struct vsp1_rwpf_memory *mem); +}; + struct vsp1_rwpf { struct vsp1_entity entity; - struct vsp1_video video; struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *alpha; + + struct vsp1_video *video; + + const struct vsp1_rwpf_operations *ops; unsigned int max_width; unsigned int max_height; + struct v4l2_pix_format_mplane format; + const struct vsp1_format_info *fmtinfo; struct { unsigned int left; unsigned int top; @@ -50,11 +70,6 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); -int vsp1_rpf_create_links(struct vsp1_device *vsp1, - struct vsp1_entity *entity); -int vsp1_wpf_create_links(struct vsp1_device *vsp1, - struct vsp1_entity *entity); - int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code); diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 6310acab60e7..cc09efbfb24f 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -151,10 +151,13 @@ static int sru_s_stream(struct v4l2_subdev *subdev, int enable) /* Take the control handler lock to ensure that the CTRL0 value won't be * changed behind our back by a set control operation. */ - mutex_lock(sru->ctrls.lock); + if (sru->entity.vsp1->info->uapi) + mutex_lock(sru->ctrls.lock); ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0) & (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK); - mutex_unlock(sru->ctrls.lock); + vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0); + if (sru->entity.vsp1->info->uapi) + mutex_unlock(sru->ctrls.lock); vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); @@ -360,7 +363,7 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) subdev = &sru->entity.subdev; v4l2_subdev_init(subdev, &sru_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s sru", dev_name(vsp1->dev)); diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index ccc8243e3493..bba67770cf95 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -29,12 +29,6 @@ * Device Access */ -static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg) -{ - return vsp1_read(uds->entity.vsp1, - reg + uds->entity.index * VI6_UDS_OFFSET); -} - static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) { vsp1_write(uds->entity.vsp1, @@ -344,7 +338,7 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) subdev = &uds->entity.subdev; v4l2_subdev_init(subdev, &uds_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u", dev_name(vsp1->dev), index); diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index b4dca57d1ae3..61ee0f92c1e5 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -14,10 +14,10 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/sched.h> #include <linux/slab.h> #include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> +#include <linux/wait.h> #include <media/media-entity.h> #include <media/v4l2-dev.h> @@ -30,6 +30,7 @@ #include "vsp1.h" #include "vsp1_bru.h" #include "vsp1_entity.h" +#include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_uds.h" #include "vsp1_video.h" @@ -47,113 +48,6 @@ * Helper functions */ -static const struct vsp1_format_info vsp1_video_formats[] = { - { V4L2_PIX_FMT_RGB332, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 8, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_ARGB444, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, true }, - { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, true }, - { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, true }, - { V4L2_PIX_FMT_XRGB555, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_BGR24, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_RGB24, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_ABGR32, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, - 1, { 32, 0, 0 }, false, false, 1, 1, true }, - { V4L2_PIX_FMT_XBGR32, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, - 1, { 32, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_ARGB32, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1, true }, - { V4L2_PIX_FMT_XRGB32, MEDIA_BUS_FMT_ARGB8888_1X32, - VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1, false }, - { V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, false, 2, 1, false }, - { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, true, 2, 1, false }, - { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, false, 2, 1, false }, - { V4L2_PIX_FMT_YVYU, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, true, 2, 1, false }, - { V4L2_PIX_FMT_NV12M, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 2, false }, - { V4L2_PIX_FMT_NV21M, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 2, false }, - { V4L2_PIX_FMT_NV16M, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 1, false }, - { V4L2_PIX_FMT_NV61M, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 1, false }, - { V4L2_PIX_FMT_YUV420M, MEDIA_BUS_FMT_AYUV8_1X32, - VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | - VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 3, { 8, 8, 8 }, false, false, 2, 2, false }, -}; - -/* - * vsp1_get_format_info - Retrieve format information for a 4CC - * @fourcc: the format 4CC - * - * Return a pointer to the format information structure corresponding to the - * given V4L2 format 4CC, or NULL if no corresponding format can be found. - */ -static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { - const struct vsp1_format_info *info = &vsp1_video_formats[i]; - - if (info->fourcc == fourcc) - return info; - } - - return NULL; -} - - static struct v4l2_subdev * vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) { @@ -184,9 +78,9 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (ret < 0) return ret == -ENOIOCTLCMD ? -EINVAL : ret; - if (video->fmtinfo->mbus != fmt.format.code || - video->format.height != fmt.format.height || - video->format.width != fmt.format.width) + if (video->rwpf->fmtinfo->mbus != fmt.format.code || + video->rwpf->format.height != fmt.format.height || + video->rwpf->format.width != fmt.format.width) return -EINVAL; return 0; @@ -277,9 +171,9 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * Pipeline Management */ -static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, - struct vsp1_rwpf *input, - struct vsp1_rwpf *output) +static int vsp1_video_pipeline_validate_branch(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *input, + struct vsp1_rwpf *output) { struct vsp1_entity *entity; struct media_entity_enum ent_enum; @@ -370,29 +264,8 @@ out: return rval; } -static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) -{ - if (pipe->bru) { - struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) - bru->inputs[i].rpf = NULL; - } - - INIT_LIST_HEAD(&pipe->entities); - pipe->state = VSP1_PIPELINE_STOPPED; - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; - pipe->uds = NULL; -} - -static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, - struct vsp1_video *video) +static int vsp1_video_pipeline_validate(struct vsp1_pipeline *pipe, + struct vsp1_video *video) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; @@ -416,10 +289,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_rwpf *rwpf; struct vsp1_entity *e; - if (is_media_entity_v4l2_io(entity)) { - pipe->num_video++; + if (is_media_entity_v4l2_io(entity)) continue; - } subdev = media_entity_to_v4l2_subdev(entity); e = to_vsp1_entity(subdev); @@ -427,12 +298,12 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, if (e->type == VSP1_ENTITY_RPF) { rwpf = to_rwpf(subdev); - pipe->inputs[pipe->num_inputs++] = rwpf; - rwpf->video.pipe_index = pipe->num_inputs; + pipe->inputs[rwpf->entity.index] = rwpf; + rwpf->video->pipe_index = ++pipe->num_inputs; } else if (e->type == VSP1_ENTITY_WPF) { rwpf = to_rwpf(subdev); - pipe->output = to_rwpf(subdev); - rwpf->video.pipe_index = 0; + pipe->output = rwpf; + rwpf->video->pipe_index = 0; } else if (e->type == VSP1_ENTITY_LIF) { pipe->lif = e; } else if (e->type == VSP1_ENTITY_BRU) { @@ -453,9 +324,12 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, /* Follow links downstream for each input and make sure the graph * contains no loop and that all branches end at the output WPF. */ - for (i = 0; i < pipe->num_inputs; ++i) { - ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i], - pipe->output); + for (i = 0; i < video->vsp1->info->rpf_count; ++i) { + if (!pipe->inputs[i]) + continue; + + ret = vsp1_video_pipeline_validate_branch(pipe, pipe->inputs[i], + pipe->output); if (ret < 0) goto error; } @@ -463,12 +337,12 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, return 0; error: - __vsp1_pipeline_cleanup(pipe); + vsp1_pipeline_reset(pipe); return ret; } -static int vsp1_pipeline_init(struct vsp1_pipeline *pipe, - struct vsp1_video *video) +static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, + struct vsp1_video *video) { int ret; @@ -476,7 +350,7 @@ static int vsp1_pipeline_init(struct vsp1_pipeline *pipe, /* If we're the first user validate and initialize the pipeline. */ if (pipe->use_count == 0) { - ret = vsp1_pipeline_validate(pipe, video); + ret = vsp1_video_pipeline_validate(pipe, video); if (ret < 0) goto done; } @@ -489,75 +363,17 @@ done: return ret; } -static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) +static void vsp1_video_pipeline_cleanup(struct vsp1_pipeline *pipe) { mutex_lock(&pipe->lock); /* If we're the last user clean up the pipeline. */ if (--pipe->use_count == 0) - __vsp1_pipeline_cleanup(pipe); + vsp1_pipeline_reset(pipe); mutex_unlock(&pipe->lock); } -static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - - vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD); - pipe->state = VSP1_PIPELINE_RUNNING; - pipe->buffers_ready = 0; -} - -static bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe) -{ - unsigned long flags; - bool stopped; - - spin_lock_irqsave(&pipe->irqlock, flags); - stopped = pipe->state == VSP1_PIPELINE_STOPPED; - spin_unlock_irqrestore(&pipe->irqlock, flags); - - return stopped; -} - -static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) -{ - struct vsp1_entity *entity; - unsigned long flags; - int ret; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); - - ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), - msecs_to_jiffies(500)); - ret = ret == 0 ? -ETIMEDOUT : 0; - - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->route && entity->route->reg) - vsp1_write(entity->vsp1, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - v4l2_subdev_call(&entity->subdev, video, s_stream, 0); - } - - return ret; -} - -static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) -{ - unsigned int mask; - - mask = ((1 << pipe->num_inputs) - 1) << 1; - if (!pipe->lif) - mask |= 1 << 0; - - return pipe->buffers_ready == mask; -} - /* * vsp1_video_complete_buffer - Complete the current buffer * @video: the video node @@ -572,12 +388,12 @@ static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) * * Return the next queued buffer or NULL if the queue is empty. */ -static struct vsp1_video_buffer * +static struct vsp1_vb2_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); - struct vsp1_video_buffer *next = NULL; - struct vsp1_video_buffer *done; + struct vsp1_vb2_buffer *next = NULL; + struct vsp1_vb2_buffer *done; unsigned long flags; unsigned int i; @@ -589,7 +405,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video) } done = list_first_entry(&video->irqqueue, - struct vsp1_video_buffer, queue); + struct vsp1_vb2_buffer, queue); /* In DU output mode reuse the buffer if the list is singular. */ if (pipe->lif && list_is_singular(&video->irqqueue)) { @@ -601,23 +417,25 @@ vsp1_video_complete_buffer(struct vsp1_video *video) if (!list_empty(&video->irqqueue)) next = list_first_entry(&video->irqqueue, - struct vsp1_video_buffer, queue); + struct vsp1_vb2_buffer, queue); spin_unlock_irqrestore(&video->irqlock, flags); done->buf.sequence = video->sequence++; done->buf.vb2_buf.timestamp = ktime_get_ns(); for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) - vb2_set_plane_payload(&done->buf.vb2_buf, i, done->length[i]); + vb2_set_plane_payload(&done->buf.vb2_buf, i, + done->mem.length[i]); vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); return next; } static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, - struct vsp1_video *video) + struct vsp1_rwpf *rwpf) { - struct vsp1_video_buffer *buf; + struct vsp1_video *video = rwpf->video; + struct vsp1_vb2_buffer *buf; unsigned long flags; buf = vsp1_video_complete_buffer(video); @@ -626,155 +444,27 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, spin_lock_irqsave(&pipe->irqlock, flags); - video->ops->queue(video, buf); + video->rwpf->ops->set_memory(video->rwpf, &buf->mem); pipe->buffers_ready |= 1 << video->pipe_index; spin_unlock_irqrestore(&pipe->irqlock, flags); } -void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) +static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) { - enum vsp1_pipeline_state state; - unsigned long flags; + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; unsigned int i; - if (pipe == NULL) - return; - /* Complete buffers on all video nodes. */ - for (i = 0; i < pipe->num_inputs; ++i) - vsp1_video_frame_end(pipe, &pipe->inputs[i]->video); - - if (!pipe->lif) - vsp1_video_frame_end(pipe, &pipe->output->video); - - spin_lock_irqsave(&pipe->irqlock, flags); - - state = pipe->state; - pipe->state = VSP1_PIPELINE_STOPPED; - - /* If a stop has been requested, mark the pipeline as stopped and - * return. - */ - if (state == VSP1_PIPELINE_STOPPING) { - wake_up(&pipe->wq); - goto done; - } - - /* Restart the pipeline if ready. */ - if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); - -done: - spin_unlock_irqrestore(&pipe->irqlock, flags); -} - -/* - * Propagate the alpha value through the pipeline. - * - * As the UDS has restricted scaling capabilities when the alpha component needs - * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha - * value. The UDS then outputs a fixed alpha value which needs to be programmed - * from the input RPF alpha. - */ -void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_entity *input, - unsigned int alpha) -{ - struct vsp1_entity *entity; - struct media_pad *pad; - - pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); - - while (pad) { - if (!is_media_entity_v4l2_subdev(pad->entity)) - break; - - entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); - - /* The BRU background color has a fixed alpha value set to 255, - * the output alpha value is thus always equal to 255. - */ - if (entity->type == VSP1_ENTITY_BRU) - alpha = 255; - - if (entity->type == VSP1_ENTITY_UDS) { - struct vsp1_uds *uds = to_uds(&entity->subdev); - - vsp1_uds_set_alpha(uds, alpha); - break; - } - - pad = &entity->pads[entity->source_pad]; - pad = media_entity_remote_pad(pad); - } -} - -void vsp1_pipelines_suspend(struct vsp1_device *vsp1) -{ - unsigned long flags; - unsigned int i; - int ret; - - /* To avoid increasing the system suspend time needlessly, loop over the - * pipelines twice, first to set them all to the stopping state, and then - * to wait for the stop to complete. - */ - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); - if (pipe == NULL) - continue; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); - } - - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); - if (pipe == NULL) + for (i = 0; i < vsp1->info->rpf_count; ++i) { + if (!pipe->inputs[i]) continue; - ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), - msecs_to_jiffies(500)); - if (ret == 0) - dev_warn(vsp1->dev, "pipeline %u stop timeout\n", - wpf->entity.index); + vsp1_video_frame_end(pipe, pipe->inputs[i]); } -} - -void vsp1_pipelines_resume(struct vsp1_device *vsp1) -{ - unsigned int i; - - /* Resume pipeline all running pipelines. */ - for (i = 0; i < vsp1->pdata.wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - if (wpf == NULL) - continue; - - pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); - if (pipe == NULL) - continue; - - if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); - } + if (!pipe->lif) + vsp1_video_frame_end(pipe, pipe->output); } /* ----------------------------------------------------------------------------- @@ -787,7 +477,7 @@ vsp1_video_queue_setup(struct vb2_queue *vq, unsigned int sizes[], void *alloc_ctxs[]) { struct vsp1_video *video = vb2_get_drv_priv(vq); - const struct v4l2_pix_format_mplane *format = &video->format; + const struct v4l2_pix_format_mplane *format = &video->rwpf->format; unsigned int i; if (*nplanes) { @@ -816,18 +506,20 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf); - const struct v4l2_pix_format_mplane *format = &video->format; + struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); + const struct v4l2_pix_format_mplane *format = &video->rwpf->format; unsigned int i; if (vb->num_planes < format->num_planes) return -EINVAL; + buf->mem.num_planes = vb->num_planes; + for (i = 0; i < vb->num_planes; ++i) { - buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); - buf->length[i] = vb2_plane_size(vb, i); + buf->mem.addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + buf->mem.length[i] = vb2_plane_size(vb, i); - if (buf->length[i] < format->plane_fmt[i].sizeimage) + if (buf->mem.length[i] < format->plane_fmt[i].sizeimage) return -EINVAL; } @@ -839,7 +531,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); - struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vbuf); + struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); unsigned long flags; bool empty; @@ -853,7 +545,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pipe->irqlock, flags); - video->ops->queue(video, buf); + video->rwpf->ops->set_memory(video->rwpf, &buf->mem); pipe->buffers_ready |= 1 << video->pipe_index; if (vb2_is_streaming(&video->queue) && @@ -863,18 +555,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } -static void vsp1_entity_route_setup(struct vsp1_entity *source) -{ - struct vsp1_entity *sink; - - if (source->route->reg == 0) - return; - - sink = container_of(source->sink, struct vsp1_entity, subdev.entity); - vsp1_write(source->vsp1, source->route->reg, - sink->route->inputs[source->sink_pad]); -} - static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); @@ -884,7 +564,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) int ret; mutex_lock(&pipe->lock); - if (pipe->stream_count == pipe->num_video - 1) { + if (pipe->stream_count == pipe->num_inputs) { if (pipe->uds) { struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); @@ -900,7 +580,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) struct vsp1_rwpf *rpf = to_rwpf(&pipe->uds_input->subdev); - uds->scale_alpha = rpf->video.fmtinfo->alpha; + uds->scale_alpha = rpf->fmtinfo->alpha; } } @@ -931,7 +611,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); - struct vsp1_video_buffer *buffer; + struct vsp1_vb2_buffer *buffer; unsigned long flags; int ret; @@ -944,7 +624,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) } mutex_unlock(&pipe->lock); - vsp1_pipeline_cleanup(pipe); + vsp1_video_pipeline_cleanup(pipe); media_entity_pipeline_stop(&video->video.entity); /* Remove all buffers from the IRQ queue. */ @@ -1004,7 +684,7 @@ vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) return -EINVAL; mutex_lock(&video->lock); - format->fmt.pix_mp = video->format; + format->fmt.pix_mp = video->rwpf->format; mutex_unlock(&video->lock); return 0; @@ -1044,8 +724,8 @@ vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) goto done; } - video->format = format->fmt.pix_mp; - video->fmtinfo = info; + video->rwpf->format = format->fmt.pix_mp; + video->rwpf->fmtinfo = info; done: mutex_unlock(&video->lock); @@ -1085,7 +765,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) goto err_stop; - ret = vsp1_pipeline_init(pipe, video); + ret = vsp1_video_pipeline_init(pipe, video); if (ret < 0) goto err_stop; @@ -1097,7 +777,7 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return 0; err_cleanup: - vsp1_pipeline_cleanup(pipe); + vsp1_video_pipeline_cleanup(pipe); err_stop: media_entity_pipeline_stop(&video->video.entity); return ret; @@ -1183,62 +863,64 @@ static struct v4l2_file_operations vsp1_video_fops = { * Initialization and Cleanup */ -int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) +struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, + struct vsp1_rwpf *rwpf) { + struct vsp1_video *video; const char *direction; int ret; - switch (video->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - direction = "output"; - video->pad.flags = MEDIA_PAD_FL_SINK; - break; + video = devm_kzalloc(vsp1->dev, sizeof(*video), GFP_KERNEL); + if (!video) + return ERR_PTR(-ENOMEM); + + rwpf->video = video; + + video->vsp1 = vsp1; + video->rwpf = rwpf; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (rwpf->entity.type == VSP1_ENTITY_RPF) { direction = "input"; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; video->pad.flags = MEDIA_PAD_FL_SOURCE; video->video.vfl_dir = VFL_DIR_TX; - break; - - default: - return -EINVAL; + } else { + direction = "output"; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + video->pad.flags = MEDIA_PAD_FL_SINK; + video->video.vfl_dir = VFL_DIR_RX; } - video->rwpf = rwpf; - mutex_init(&video->lock); spin_lock_init(&video->irqlock); INIT_LIST_HEAD(&video->irqqueue); - mutex_init(&video->pipe.lock); - spin_lock_init(&video->pipe.irqlock); - INIT_LIST_HEAD(&video->pipe.entities); - init_waitqueue_head(&video->pipe.wq); - video->pipe.state = VSP1_PIPELINE_STOPPED; + vsp1_pipeline_init(&video->pipe); + video->pipe.frame_end = vsp1_video_pipeline_frame_end; /* Initialize the media entity... */ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); if (ret < 0) - return ret; + return ERR_PTR(ret); /* ... and the format ... */ - video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); - video->format.pixelformat = video->fmtinfo->fourcc; - video->format.colorspace = V4L2_COLORSPACE_SRGB; - video->format.field = V4L2_FIELD_NONE; - video->format.width = VSP1_VIDEO_DEF_WIDTH; - video->format.height = VSP1_VIDEO_DEF_HEIGHT; - video->format.num_planes = 1; - video->format.plane_fmt[0].bytesperline = - video->format.width * video->fmtinfo->bpp[0] / 8; - video->format.plane_fmt[0].sizeimage = - video->format.plane_fmt[0].bytesperline * video->format.height; + rwpf->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + rwpf->format.pixelformat = rwpf->fmtinfo->fourcc; + rwpf->format.colorspace = V4L2_COLORSPACE_SRGB; + rwpf->format.field = V4L2_FIELD_NONE; + rwpf->format.width = VSP1_VIDEO_DEF_WIDTH; + rwpf->format.height = VSP1_VIDEO_DEF_HEIGHT; + rwpf->format.num_planes = 1; + rwpf->format.plane_fmt[0].bytesperline = + rwpf->format.width * rwpf->fmtinfo->bpp[0] / 8; + rwpf->format.plane_fmt[0].sizeimage = + rwpf->format.plane_fmt[0].bytesperline * rwpf->format.height; /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", - rwpf->subdev.name, direction); + rwpf->entity.subdev.name, direction); video->video.vfl_type = VFL_TYPE_GRABBER; video->video.release = video_device_release_empty; video->video.ioctl_ops = &vsp1_video_ioctl_ops; @@ -1256,7 +938,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; video->queue.lock = &video->lock; video->queue.drv_priv = video; - video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer); + video->queue.buf_struct_size = sizeof(struct vsp1_vb2_buffer); video->queue.ops = &vsp1_video_queue_qops; video->queue.mem_ops = &vb2_dma_contig_memops; video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; @@ -1274,12 +956,12 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) goto error; } - return 0; + return video; error: vb2_dma_contig_cleanup_ctx(video->alloc_ctx); vsp1_video_cleanup(video); - return ret; + return ERR_PTR(ret); } void vsp1_video_cleanup(struct vsp1_video *video) diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index a929aa81cdbf..64abd39ee1e7 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -15,115 +15,34 @@ #include <linux/list.h> #include <linux/spinlock.h> -#include <linux/wait.h> -#include <media/media-entity.h> #include <media/videobuf2-v4l2.h> -struct vsp1_video; +#include "vsp1_pipe.h" +#include "vsp1_rwpf.h" -/* - * struct vsp1_format_info - VSP1 video format description - * @mbus: media bus format code - * @fourcc: V4L2 pixel format FCC identifier - * @planes: number of planes - * @bpp: bits per pixel - * @hwfmt: VSP1 hardware format - * @swap_yc: the Y and C components are swapped (Y comes before C) - * @swap_uv: the U and V components are swapped (V comes before U) - * @hsub: horizontal subsampling factor - * @vsub: vertical subsampling factor - * @alpha: has an alpha channel - */ -struct vsp1_format_info { - u32 fourcc; - unsigned int mbus; - unsigned int hwfmt; - unsigned int swap; - unsigned int planes; - unsigned int bpp[3]; - bool swap_yc; - bool swap_uv; - unsigned int hsub; - unsigned int vsub; - bool alpha; -}; - -enum vsp1_pipeline_state { - VSP1_PIPELINE_STOPPED, - VSP1_PIPELINE_RUNNING, - VSP1_PIPELINE_STOPPING, -}; - -/* - * struct vsp1_pipeline - A VSP1 hardware pipeline - * @media: the media pipeline - * @irqlock: protects the pipeline state - * @lock: protects the pipeline use count and stream count - */ -struct vsp1_pipeline { - struct media_pipeline pipe; - - spinlock_t irqlock; - enum vsp1_pipeline_state state; - wait_queue_head_t wq; - - struct mutex lock; - unsigned int use_count; - unsigned int stream_count; - unsigned int buffers_ready; - - unsigned int num_video; - unsigned int num_inputs; - struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; - struct vsp1_rwpf *output; - struct vsp1_entity *bru; - struct vsp1_entity *lif; - struct vsp1_entity *uds; - struct vsp1_entity *uds_input; - - struct list_head entities; -}; - -static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) -{ - if (likely(e->pipe)) - return container_of(e->pipe, struct vsp1_pipeline, pipe); - else - return NULL; -} - -struct vsp1_video_buffer { +struct vsp1_vb2_buffer { struct vb2_v4l2_buffer buf; struct list_head queue; - - dma_addr_t addr[3]; - unsigned int length[3]; + struct vsp1_rwpf_memory mem; }; -static inline struct vsp1_video_buffer * -to_vsp1_video_buffer(struct vb2_v4l2_buffer *vbuf) +static inline struct vsp1_vb2_buffer * +to_vsp1_vb2_buffer(struct vb2_v4l2_buffer *vbuf) { - return container_of(vbuf, struct vsp1_video_buffer, buf); + return container_of(vbuf, struct vsp1_vb2_buffer, buf); } -struct vsp1_video_operations { - void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf); -}; - struct vsp1_video { + struct list_head list; struct vsp1_device *vsp1; - struct vsp1_entity *rwpf; - - const struct vsp1_video_operations *ops; + struct vsp1_rwpf *rwpf; struct video_device video; enum v4l2_buf_type type; struct media_pad pad; struct mutex lock; - struct v4l2_pix_format_mplane format; - const struct vsp1_format_info *fmtinfo; struct vsp1_pipeline pipe; unsigned int pipe_index; @@ -140,16 +59,8 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) return container_of(vdev, struct vsp1_video, video); } -int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf); +struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, + struct vsp1_rwpf *rwpf); void vsp1_video_cleanup(struct vsp1_video *video); -void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); - -void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_entity *input, - unsigned int alpha); - -void vsp1_pipelines_suspend(struct vsp1_device *vsp1); -void vsp1_pipelines_resume(struct vsp1_device *vsp1); - #endif /* __VSP1_VIDEO_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index cbf514a6582d..c78d4af50fcf 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -34,8 +34,8 @@ static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg) static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) { - vsp1_write(wpf->entity.vsp1, - reg + wpf->entity.index * VI6_WPF_OFFSET, data); + vsp1_mod_write(&wpf->entity, + reg + wpf->entity.index * VI6_WPF_OFFSET, data); } /* ----------------------------------------------------------------------------- @@ -88,7 +88,8 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) if (!enable) { vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); - vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0); + vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + + VI6_WPF_SRCRPF, 0); return 0; } @@ -97,9 +98,12 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) * inputs as sub-layers and select the virtual RPF as the master * layer. */ - for (i = 0; i < pipe->num_inputs; ++i) { + for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; + if (!input) + continue; + srcrpf |= (!pipe->bru && pipe->num_inputs == 1) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); @@ -112,7 +116,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) /* Destination stride. */ if (!pipe->lif) { - struct v4l2_pix_format_mplane *format = &wpf->video.format; + struct v4l2_pix_format_mplane *format = &wpf->format; vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, format->plane_fmt[0].bytesperline); @@ -130,7 +134,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) /* Format */ if (!pipe->lif) { - const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo; + const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; @@ -151,15 +155,17 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) /* Take the control handler lock to ensure that the PDV value won't be * changed behind our back by a set control operation. */ - mutex_lock(wpf->ctrls.lock); - outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK; + if (vsp1->info->uapi) + mutex_lock(wpf->ctrls.lock); + outfmt |= wpf->alpha->cur.val << VI6_WPF_OUTFMT_PDV_SHIFT; vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); - mutex_unlock(wpf->ctrls.lock); + if (vsp1->info->uapi) + mutex_unlock(wpf->ctrls.lock); - vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), - VI6_DPR_WPF_FPORCH_FP_WPFN); + vsp1_mod_write(&wpf->entity, VI6_DPR_WPF_FPORCH(wpf->entity.index), + VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0); + vsp1_mod_write(&wpf->entity, VI6_WPF_WRBCK_CTRL, 0); /* Enable interrupts */ vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0); @@ -195,20 +201,17 @@ static struct v4l2_subdev_ops wpf_ops = { * Video Device Operations */ -static void wpf_vdev_queue(struct vsp1_video *video, - struct vsp1_video_buffer *buf) +static void wpf_set_memory(struct vsp1_rwpf *wpf, struct vsp1_rwpf_memory *mem) { - struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video); - - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]); - if (buf->buf.vb2_buf.num_planes > 1) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]); - if (buf->buf.vb2_buf.num_planes > 2) - vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]); + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, mem->addr[0]); + if (mem->num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, mem->addr[1]); + if (mem->num_planes > 2) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, mem->addr[2]); } -static const struct vsp1_video_operations wpf_vdev_ops = { - .queue = wpf_vdev_queue, +static const struct vsp1_rwpf_operations wpf_vdev_ops = { + .set_memory = wpf_set_memory, }; /* ----------------------------------------------------------------------------- @@ -218,7 +221,6 @@ static const struct vsp1_video_operations wpf_vdev_ops = { struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) { struct v4l2_subdev *subdev; - struct vsp1_video *video; struct vsp1_rwpf *wpf; int ret; @@ -226,6 +228,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) if (wpf == NULL) return ERR_PTR(-ENOMEM); + wpf->ops = &wpf_vdev_ops; + wpf->max_width = WPF_MAX_WIDTH; wpf->max_height = WPF_MAX_HEIGHT; @@ -240,7 +244,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) subdev = &wpf->entity.subdev; v4l2_subdev_init(subdev, &wpf_ops); - subdev->entity.ops = &vsp1_media_ops; + subdev->entity.ops = &vsp1->media_ops; subdev->internal_ops = &vsp1_subdev_internal_ops; snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u", dev_name(vsp1->dev), index); @@ -251,8 +255,9 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) /* Initialize the control handler. */ v4l2_ctrl_handler_init(&wpf->ctrls, 1); - v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, - 0, 255, 1, 255); + wpf->alpha = v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, + 0, 255, 1, 255); wpf->entity.subdev.ctrl_handler = &wpf->ctrls; @@ -263,48 +268,9 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) goto error; } - /* Initialize the video device. */ - video = &wpf->video; - - video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - video->vsp1 = vsp1; - video->ops = &wpf_vdev_ops; - - ret = vsp1_video_init(video, &wpf->entity); - if (ret < 0) - goto error; - - wpf->entity.video = video; - wpf->entity.sink = &wpf->video.video.entity; - return wpf; error: vsp1_entity_destroy(&wpf->entity); return ERR_PTR(ret); } - -/* - * vsp1_wpf_create_links() - RPF pads links creation - * @vsp1: Pointer to VSP1 device - * @entity: Pointer to VSP1 entity - * - * return negative error code or zero on success - */ -int vsp1_wpf_create_links(struct vsp1_device *vsp1, - struct vsp1_entity *entity) -{ - struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); - unsigned int flags; - - /* Connect the video device to the WPF. All connections are immutable - * except for the WPF0 source link if a LIF is present. - */ - flags = MEDIA_LNK_FL_ENABLED; - if (!(vsp1->pdata.features & VSP1_HAS_LIF) || entity->index != 0) - flags |= MEDIA_LNK_FL_IMMUTABLE; - - return media_create_pad_link(&wpf->entity.subdev.entity, - RWPF_PAD_SOURCE, - &wpf->video.video.entity, 0, flags); -} |