Re: [PATCH 2/4] media: platform: dwc: Support for DW CSI-2 Host

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Hi Ramiro,

My sincere apologies for the long delay in reviewing this. The good news is that
I should have more time for reviews going forward, so I hope I'll be a lot quicker
in the future.

On 03/07/2017 03:37 PM, Ramiro Oliveira wrote:
> Add support for the Synopsys DesignWare CSI-2 Host
> 
> Signed-off-by: Ramiro Oliveira <roliveir@xxxxxxxxxxxx>
> ---
>  MAINTAINERS                              |   7 +
>  drivers/media/platform/Kconfig           |   1 +
>  drivers/media/platform/Makefile          |   2 +
>  drivers/media/platform/dwc/Kconfig       |   5 +
>  drivers/media/platform/dwc/Makefile      |   1 +
>  drivers/media/platform/dwc/dw_mipi_csi.c | 653 +++++++++++++++++++++++++++++++
>  drivers/media/platform/dwc/dw_mipi_csi.h | 181 +++++++++
>  include/media/dwc/csi_host_platform.h    |  97 +++++
>  8 files changed, 947 insertions(+)
>  create mode 100644 drivers/media/platform/dwc/Kconfig
>  create mode 100644 drivers/media/platform/dwc/Makefile
>  create mode 100644 drivers/media/platform/dwc/dw_mipi_csi.c
>  create mode 100644 drivers/media/platform/dwc/dw_mipi_csi.h
>  create mode 100644 include/media/dwc/csi_host_platform.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5badfd33e51f..19673dad43b4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11061,6 +11061,13 @@ S:	Maintained
>  F:	drivers/staging/media/st-cec/
>  F:	Documentation/devicetree/bindings/media/stih-cec.txt
>  
> +SYNOPSYS DESIGNWARE CSI-2 HOST VIDEO PLATFORM
> +M:	Ramiro Oliveira <roliveir@xxxxxxxxxxxx>
> +L:	linux-media@xxxxxxxxxxxxxxx
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Maintained
> +F:	drivers/media/platform/dwc/
> +
>  SHARED MEMORY COMMUNICATIONS (SMC) SOCKETS
>  M:	Ursula Braun <ubraun@xxxxxxxxxxxxxxxxxx>
>  L:	linux-s390@xxxxxxxxxxxxxxx
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 53f6f12bff0d..4b6e00da763f 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -120,6 +120,7 @@ source "drivers/media/platform/am437x/Kconfig"
>  source "drivers/media/platform/xilinx/Kconfig"
>  source "drivers/media/platform/rcar-vin/Kconfig"
>  source "drivers/media/platform/atmel/Kconfig"
> +source "drivers/media/platform/dwc/Kconfig"
>  
>  config VIDEO_TI_CAL
>  	tristate "TI CAL (Camera Adaptation Layer) driver"
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 8959f6e6692a..95eae2772c1f 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -64,6 +64,8 @@ obj-$(CONFIG_VIDEO_RCAR_VIN)		+= rcar-vin/
>  
>  obj-$(CONFIG_VIDEO_ATMEL_ISC)		+= atmel/
>  
> +obj-$(CONFIG_CSI_VIDEO_PLATFORM)	+= dwc/
> +
>  ccflags-y += -I$(srctree)/drivers/media/i2c
>  
>  obj-$(CONFIG_VIDEO_MEDIATEK_VPU)	+= mtk-vpu/
> diff --git a/drivers/media/platform/dwc/Kconfig b/drivers/media/platform/dwc/Kconfig
> new file mode 100644
> index 000000000000..2cd13d23f897
> --- /dev/null
> +++ b/drivers/media/platform/dwc/Kconfig
> @@ -0,0 +1,5 @@
> +config DWC_MIPI_CSI2_HOST
> +	tristate "SNPS DWC MIPI CSI2 Host"
> +	select GENERIC_PHY
> +	help
> +	  This is a V4L2 driver for Synopsys Designware MIPI CSI-2 Host.
> diff --git a/drivers/media/platform/dwc/Makefile b/drivers/media/platform/dwc/Makefile
> new file mode 100644
> index 000000000000..5eb076a55123
> --- /dev/null
> +++ b/drivers/media/platform/dwc/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_DWC_MIPI_CSI2_HOST)	+= dw_mipi_csi.o
> diff --git a/drivers/media/platform/dwc/dw_mipi_csi.c b/drivers/media/platform/dwc/dw_mipi_csi.c
> new file mode 100644
> index 000000000000..6905def40a07
> --- /dev/null
> +++ b/drivers/media/platform/dwc/dw_mipi_csi.c
> @@ -0,0 +1,653 @@
> +/*
> + * DWC MIPI CSI-2 Host device driver
> + *
> + * Copyright (C) 2016 Synopsys, Inc. All rights reserved.
> + * Author: Ramiro Oliveira <ramiro.oliveira@xxxxxxxxxxxx>
> + *
> + * 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 "dw_mipi_csi.h"
> +
> +/**
> + * @short Video formats supported by the MIPI CSI-2
> + */
> +static const struct mipi_fmt dw_mipi_csi_formats[] = {
> +	{
> +		/* RAW 8 */
> +		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
> +		.depth = 8,
> +	},
> +	{
> +		/* RAW 10 */
> +		.code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE,
> +		.depth = 10,
> +	},
> +	{
> +		/* RGB 565 */
> +		.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
> +		.depth = 16,
> +	},
> +	{
> +		/* BGR 565 */
> +		.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
> +		.depth = 16,
> +	},
> +	{
> +		/* RGB 888 */
> +		.code = MEDIA_BUS_FMT_RGB888_2X12_LE,
> +		.depth = 24,
> +	},
> +	{
> +		/* BGR 888 */
> +		.code = MEDIA_BUS_FMT_RGB888_2X12_BE,
> +		.depth = 24,
> +	},
> +};
> +
> +static struct mipi_csi_dev *sd_to_mipi_csi_dev(struct v4l2_subdev *sdev)
> +{
> +	return container_of(sdev, struct mipi_csi_dev, sd);
> +}
> +
> +static void dw_mipi_csi_write(struct mipi_csi_dev *dev,
> +		  unsigned int address, unsigned int data)
> +{
> +	iowrite32(data, dev->base_address + address);
> +}
> +
> +static u32 dw_mipi_csi_read(struct mipi_csi_dev *dev, unsigned long address)
> +{
> +	return ioread32(dev->base_address + address);
> +}
> +
> +static void dw_mipi_csi_write_part(struct mipi_csi_dev *dev,
> +		       unsigned long address, unsigned long data,
> +		       unsigned char shift, unsigned char width)
> +{
> +	u32 mask = (1 << width) - 1;
> +	u32 temp = dw_mipi_csi_read(dev, address);
> +
> +	temp &= ~(mask << shift);
> +	temp |= (data & mask) << shift;
> +	dw_mipi_csi_write(dev, address, temp);
> +}
> +
> +static const struct mipi_fmt *
> +find_dw_mipi_csi_format(struct v4l2_mbus_framefmt *mf)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(dw_mipi_csi_formats); i++)
> +		if (mf->code == dw_mipi_csi_formats[i].code)
> +			return &dw_mipi_csi_formats[i];
> +	return NULL;
> +}
> +
> +static void dw_mipi_csi_reset(struct mipi_csi_dev *dev)
> +{
> +	dw_mipi_csi_write(dev, R_CSI2_CTRL_RESETN, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_CTRL_RESETN, 1);
> +}
> +
> +static int dw_mipi_csi_mask_irq_power_off(struct mipi_csi_dev *dev)
> +{
> +	/* set only one lane (lane 0) as active (ON) */
> +	dw_mipi_csi_write(dev, R_CSI2_N_LANES, 0);
> +
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PHY_FATAL, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PKT_FATAL, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_FRAME_FATAL, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PHY, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PKT, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_LINE, 0);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_IPI, 0);
> +
> +	dw_mipi_csi_write(dev, R_CSI2_CTRL_RESETN, 0);
> +
> +	return 0;
> +

Spurious empty line.

> +}
> +
> +static int dw_mipi_csi_hw_stdby(struct mipi_csi_dev *dev)
> +{
> +	/* set only one lane (lane 0) as active (ON) */
> +	dw_mipi_csi_reset(dev);
> +
> +	dw_mipi_csi_write(dev, R_CSI2_N_LANES, 0);
> +
> +	phy_init(dev->phy);
> +
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PHY_FATAL, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PKT_FATAL, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_FRAME_FATAL, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PHY, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_PKT, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_LINE, 0xFFFFFFFF);
> +	dw_mipi_csi_write(dev, R_CSI2_MASK_INT_IPI, 0xFFFFFFFF);
> +
> +	return 0;
> +

Ditto.

> +}
> +
> +static void dw_mipi_csi_set_ipi_fmt(struct mipi_csi_dev *csi_dev)
> +{
> +	struct device *dev = &csi_dev->pdev->dev;
> +
> +	switch (csi_dev->fmt->code) {
> +	case MEDIA_BUS_FMT_RGB565_2X8_BE:
> +	case MEDIA_BUS_FMT_RGB565_2X8_LE:
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_DATA_TYPE, CSI_2_RGB565);
> +		dev_dbg(dev, "DT: RGB 565");
> +		break;
> +
> +	case MEDIA_BUS_FMT_RGB888_2X12_LE:
> +	case MEDIA_BUS_FMT_RGB888_2X12_BE:
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_DATA_TYPE, CSI_2_RGB888);
> +		dev_dbg(dev, "DT: RGB 888");
> +		break;
> +	case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_DATA_TYPE, CSI_2_RAW10);
> +		dev_dbg(dev, "DT: RAW 10");
> +		break;
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_DATA_TYPE, CSI_2_RAW8);
> +		dev_dbg(dev, "DT: RAW 8");
> +		break;
> +	default:
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_DATA_TYPE, CSI_2_RGB565);
> +		dev_dbg(dev, "Error");
> +		break;
> +	}
> +}
> +
> +static void __dw_mipi_csi_fill_timings(struct mipi_csi_dev *dev,
> +			   const struct v4l2_bt_timings *bt)
> +{
> +
> +	if (bt == NULL)
> +		return;
> +
> +	dev->hw.hsa = bt->hsync;
> +	dev->hw.hbp = bt->hbackporch;
> +	dev->hw.hsd = bt->hsync;
> +	dev->hw.htotal = bt->height + bt->vfrontporch +
> +	    bt->vsync + bt->vbackporch;

Use V4L2_DV_BT_FRAME_HEIGHT define.

> +	dev->hw.vsa = bt->vsync;
> +	dev->hw.vbp = bt->vbackporch;
> +	dev->hw.vfp = bt->vfrontporch;
> +	dev->hw.vactive = bt->height;
> +}
> +
> +static void dw_mipi_csi_start(struct mipi_csi_dev *csi_dev)
> +{
> +	const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[0].bt;
> +	struct device *dev = &csi_dev->pdev->dev;
> +
> +	__dw_mipi_csi_fill_timings(csi_dev, bt);
> +
> +	dw_mipi_csi_write(csi_dev, R_CSI2_N_LANES, (csi_dev->hw.num_lanes - 1));
> +	dev_dbg(dev, "N Lanes: %d\n", csi_dev->hw.num_lanes);
> +
> +	/*IPI Related Configuration */
> +	if ((csi_dev->hw.output_type == IPI_OUT)
> +	    || (csi_dev->hw.output_type == BOTH_OUT)) {
> +
> +		dw_mipi_csi_write_part(csi_dev, R_CSI2_IPI_MODE,
> +					csi_dev->hw.ipi_mode, 0, 1);
> +		dev_dbg(dev, "IPI MODE: %d\n", csi_dev->hw.ipi_mode);
> +
> +		dw_mipi_csi_write_part(csi_dev, R_CSI2_IPI_MODE,
> +				       csi_dev->hw.ipi_color_mode, 8, 1);
> +		dev_dbg(dev, "Color Mode: %d\n", csi_dev->hw.ipi_color_mode);
> +
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_VCID,
> +					csi_dev->hw.virtual_ch);
> +		dev_dbg(dev, "Virtual Channel: %d\n", csi_dev->hw.virtual_ch);
> +
> +		dw_mipi_csi_write_part(csi_dev, R_CSI2_IPI_MEM_FLUSH,
> +				       csi_dev->hw.ipi_auto_flush, 8, 1);
> +		dev_dbg(dev, "Auto-flush: %d\n", csi_dev->hw.ipi_auto_flush);
> +
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_HSA_TIME,
> +					csi_dev->hw.hsa);
> +		dev_dbg(dev, "HSA: %d\n", csi_dev->hw.hsa);
> +
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_HBP_TIME,
> +					csi_dev->hw.hbp);
> +		dev_dbg(dev, "HBP: %d\n", csi_dev->hw.hbp);
> +
> +		dw_mipi_csi_write(csi_dev, R_CSI2_IPI_HSD_TIME,
> +					csi_dev->hw.hsd);
> +		dev_dbg(dev, "HSD: %d\n", csi_dev->hw.hsd);
> +
> +		if (csi_dev->hw.ipi_mode == AUTO_TIMING) {
> +			dw_mipi_csi_write(csi_dev, R_CSI2_IPI_HLINE_TIME,
> +					  csi_dev->hw.htotal);
> +			dev_dbg(dev, "H total: %d\n", csi_dev->hw.htotal);
> +
> +			dw_mipi_csi_write(csi_dev, R_CSI2_IPI_VSA_LINES,
> +					  csi_dev->hw.vsa);
> +			dev_dbg(dev, "VSA: %d\n", csi_dev->hw.vsa);
> +
> +			dw_mipi_csi_write(csi_dev, R_CSI2_IPI_VBP_LINES,
> +					  csi_dev->hw.vbp);
> +			dev_dbg(dev, "VBP: %d\n", csi_dev->hw.vbp);
> +
> +			dw_mipi_csi_write(csi_dev, R_CSI2_IPI_VFP_LINES,
> +					  csi_dev->hw.vfp);
> +			dev_dbg(dev, "VFP: %d\n", csi_dev->hw.vfp);
> +
> +			dw_mipi_csi_write(csi_dev, R_CSI2_IPI_VACTIVE_LINES,
> +					  csi_dev->hw.vactive);
> +			dev_dbg(dev, "V Active: %d\n", csi_dev->hw.vactive);
> +		}
> +	}
> +
> +	phy_power_on(csi_dev->phy);
> +}
> +
> +static int dw_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
> +					struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index >= ARRAY_SIZE(dw_mipi_csi_formats))
> +		return -EINVAL;
> +
> +	code->code = dw_mipi_csi_formats[code->index].code;
> +	return 0;
> +}
> +
> +static struct mipi_fmt const *
> +dw_mipi_csi_try_format(struct v4l2_mbus_framefmt *mf)
> +{
> +	struct mipi_fmt const *fmt;
> +
> +	fmt = find_dw_mipi_csi_format(mf);
> +	if (fmt == NULL)
> +		fmt = &dw_mipi_csi_formats[0];
> +
> +	mf->code = fmt->code;
> +	return fmt;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__dw_mipi_csi_get_format(struct mipi_csi_dev *dev,
> +			 struct v4l2_subdev_pad_config *cfg,
> +			 enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return cfg ? v4l2_subdev_get_try_format(&dev->sd, cfg,
> +							0) : NULL;
> +
> +	return &dev->format;
> +}
> +
> +static int
> +dw_mipi_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
> +		    struct v4l2_subdev_format *fmt)
> +{
> +	struct mipi_csi_dev *dev = sd_to_mipi_csi_dev(sd);
> +	struct mipi_fmt const *dev_fmt;
> +	struct v4l2_mbus_framefmt *mf;
> +	unsigned int i = 0;
> +	const struct v4l2_bt_timings *bt_r = &v4l2_dv_timings_presets[0].bt;
> +
> +	mf = __dw_mipi_csi_get_format(dev, cfg, fmt->which);
> +
> +	dev_fmt = dw_mipi_csi_try_format(&fmt->format);
> +	if (dev_fmt) {
> +		*mf = fmt->format;
> +		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
> +			dev->fmt = dev_fmt;
> +		dw_mipi_csi_set_ipi_fmt(dev);
> +	}
> +	while (v4l2_dv_timings_presets[i].bt.width) {
> +		const struct v4l2_bt_timings *bt =
> +		    &v4l2_dv_timings_presets[i].bt;

Add empty line here.

> +		if (mf->width == bt->width && mf->height == bt->width) {

This is way too generic. There are many preset timings that have the same
width and height but different blanking periods.

I am really not sure how this is supposed to work. If you want to support
HDMI here, then I would expect to see support for the s_dv_timings op and friends
in this driver. And I don't see support for that in the host driver either.

Is this a generic csi driver, or specific for hdmi? Or supposed to handle both?

Can you give some background and clarification of this?

Before I do any more code review I need to have a clearer understanding of this.

Regards,

	Hans

> +			__dw_mipi_csi_fill_timings(dev, bt);
> +			return 0;
> +		}
> +		i++;
> +	}
> +
> +	__dw_mipi_csi_fill_timings(dev, bt_r);
> +	return 0;
> +
> +}
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux