Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1415786 > unrolled thread
| Started by | Songjun Wu <songjun.wu@atmel.com> |
|---|---|
| First post | 2016-06-07 09:30 +0200 |
| Last post | 2016-06-08 23:40 +0200 |
| Articles | 5 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH v4 0/2] [media] atmel-isc: add driver for Atmel ISC Songjun Wu <songjun.wu@atmel.com> - 2016-06-07 09:30 +0200
[PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver Songjun Wu <songjun.wu@atmel.com> - 2016-06-07 09:30 +0200
Re: [PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver Rob Herring <robh@kernel.org> - 2016-06-08 22:10 +0200
Re: [PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver Boris Brezillon <boris.brezillon@free-electrons.com> - 2016-06-09 00:00 +0200
Re: [PATCH v4 1/2] [media] atmel-isc: add the Image Sensor Controller code Boris Brezillon <boris.brezillon@free-electrons.com> - 2016-06-08 23:40 +0200
| From | Songjun Wu <songjun.wu@atmel.com> |
|---|---|
| Date | 2016-06-07 09:30 +0200 |
| Subject | [PATCH v4 0/2] [media] atmel-isc: add driver for Atmel ISC |
| Message-ID | <rHjCy-85S-7@gated-at.bofh.it> |
The Image Sensor Controller driver includes two parts. 1) Driver code to implement the ISC function. 2) Device tree binding documentation, it describes how to add the ISC in device tree. Changes in v4: - Modify the isc clock code since the dt is changed. - Remove the isc clock nodes. Changes in v3: - Add pm runtime feature. - Modify the isc clock code since the dt is changed. - Remove the 'atmel,sensor-preferred'. - Modify the isc clock node according to the Rob's remarks. Changes in v2: - Add "depends on COMMON_CLK" and "VIDEO_V4L2_SUBDEV_API" in Kconfig file. - Correct typos and coding style according to Laurent's remarks - Delete the loop while in 'isc_clk_enable' function. - Add the code to support VIDIOC_CREATE_BUFS in 'isc_queue_setup' function. - Invoke isc_config to configure register in 'isc_start_streaming' function. - Add the struct completion 'comp' to synchronize with the frame end interrupt in 'isc_stop_streaming' function. - Check the return value of the clk_prepare_enable in 'isc_open' function. - Set the default format in 'isc_open' function. - Add an exit condition in the loop while in 'isc_config'. - Delete the hardware setup operation in 'isc_set_format'. - Refuse format modification during streaming in 'isc_s_fmt_vid_cap' function. - Invoke v4l2_subdev_alloc_pad_config to allocate and initialize the pad config in 'isc_async_complete' function. - Remove the '.owner = THIS_MODULE,' in atmel_isc_driver. - Replace the module_platform_driver_probe() with module_platform_driver(). - Remove the unit address of the endpoint. - Add the unit address to the clock node. - Avoid using underscores in node names. - Drop the "0x" in the unit address of the i2c node. - Modify the description of 'atmel,sensor-preferred'. - Add the description for the ISC internal clock. Songjun Wu (2): [media] atmel-isc: add the Image Sensor Controller code [media] atmel-isc: DT binding for Image Sensor Controller driver .../devicetree/bindings/media/atmel-isc.txt | 69 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 2 + drivers/media/platform/atmel/Kconfig | 9 + drivers/media/platform/atmel/Makefile | 1 + drivers/media/platform/atmel/atmel-isc-regs.h | 276 ++++ drivers/media/platform/atmel/atmel-isc.c | 1580 ++++++++++++++++++++ 7 files changed, 1938 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/atmel-isc.txt create mode 100644 drivers/media/platform/atmel/Kconfig create mode 100644 drivers/media/platform/atmel/Makefile create mode 100644 drivers/media/platform/atmel/atmel-isc-regs.h create mode 100644 drivers/media/platform/atmel/atmel-isc.c -- 2.7.4
[toc] | [next] | [standalone]
| From | Songjun Wu <songjun.wu@atmel.com> |
|---|---|
| Date | 2016-06-07 09:30 +0200 |
| Subject | [PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver |
| Message-ID | <rHjCy-85S-13@gated-at.bofh.it> |
| In reply to | #1415786 |
DT binding documentation for ISC driver.
Signed-off-by: Songjun Wu <songjun.wu@atmel.com>
---
Changes in v4:
- Remove the isc clock nodes.
Changes in v3:
- Remove the 'atmel,sensor-preferred'.
- Modify the isc clock node according to the Rob's remarks.
Changes in v2:
- Remove the unit address of the endpoint.
- Add the unit address to the clock node.
- Avoid using underscores in node names.
- Drop the "0x" in the unit address of the i2c node.
- Modify the description of 'atmel,sensor-preferred'.
- Add the description for the ISC internal clock.
.../devicetree/bindings/media/atmel-isc.txt | 69 ++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/atmel-isc.txt
diff --git a/Documentation/devicetree/bindings/media/atmel-isc.txt b/Documentation/devicetree/bindings/media/atmel-isc.txt
new file mode 100644
index 0000000..3f83524
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/atmel-isc.txt
@@ -0,0 +1,69 @@
+Atmel Image Sensor Controller (ISC)
+----------------------------------------------
+
+Required properties for ISC:
+- compatible
+ Must be "atmel,sama5d2-isc".
+- reg
+ Physical base address and length of the registers set for the device.
+- interrupts
+ Should contain IRQ line for the ISC.
+- clocks
+ List of clock specifiers, corresponding to entries in
+ the clock-names property;
+ Please refer to clock-bindings.txt.
+- clock-names
+ Required elements: "hclock".
+- #clock-cells
+ Should be 0.
+- clock-output-names
+ Should contain the name of the clock driving the sensor master clock.
+- pinctrl-names, pinctrl-0
+ Please refer to pinctrl-bindings.txt.
+
+
+ISC supports a single port node with parallel bus. It should contain one
+'port' child node with child 'endpoint' node. Please refer to the bindings
+defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+isc: isc@f0008000 {
+ compatible = "atmel,sama5d2-isc";
+ reg = <0xf0008000 0x4000>;
+ interrupts = <46 IRQ_TYPE_LEVEL_HIGH 5>;
+ clocks = <&isc_clk>, <&iscck>, <&isc_gclk>;
+ clock-names = "hclock";
+ #clock-cells = <0>;
+ clock-output-names = "isc-mck";
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ isc_0: endpoint {
+ remote-endpoint = <&ov7740_0>;
+ hsync-active = <1>;
+ vsync-active = <0>;
+ pclk-sample = <1>;
+ };
+ };
+
+
+};
+
+i2c1: i2c@fc028000 {
+ ov7740: camera@21 {
+ compatible = "ovti,ov7740";
+ reg = <0x21>;
+
+ clocks = <&isc>;
+ clock-names = "xvclk";
+ assigned-clocks = <&isc>;
+ assigned-clock-rates = <24000000>;
+
+ port {
+ ov7740_0: endpoint {
+ remote-endpoint = <&isc_0>;
+ };
+ };
+};
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-06-08 22:10 +0200 |
| Subject | Re: [PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver |
| Message-ID | <rHRXz-4Km-11@gated-at.bofh.it> |
| In reply to | #1415787 |
On Tue, Jun 07, 2016 at 03:11:53PM +0800, Songjun Wu wrote:
> DT binding documentation for ISC driver.
>
> Signed-off-by: Songjun Wu <songjun.wu@atmel.com>
> ---
>
> Changes in v4:
> - Remove the isc clock nodes.
>
> Changes in v3:
> - Remove the 'atmel,sensor-preferred'.
> - Modify the isc clock node according to the Rob's remarks.
>
> Changes in v2:
> - Remove the unit address of the endpoint.
> - Add the unit address to the clock node.
> - Avoid using underscores in node names.
> - Drop the "0x" in the unit address of the i2c node.
> - Modify the description of 'atmel,sensor-preferred'.
> - Add the description for the ISC internal clock.
>
> .../devicetree/bindings/media/atmel-isc.txt | 69 ++++++++++++++++++++++
> 1 file changed, 69 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/atmel-isc.txt
Almost there...
> diff --git a/Documentation/devicetree/bindings/media/atmel-isc.txt b/Documentation/devicetree/bindings/media/atmel-isc.txt
> new file mode 100644
> index 0000000..3f83524
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/atmel-isc.txt
> @@ -0,0 +1,69 @@
> +Atmel Image Sensor Controller (ISC)
> +----------------------------------------------
> +
> +Required properties for ISC:
> +- compatible
> + Must be "atmel,sama5d2-isc".
> +- reg
> + Physical base address and length of the registers set for the device.
> +- interrupts
> + Should contain IRQ line for the ISC.
> +- clocks
> + List of clock specifiers, corresponding to entries in
> + the clock-names property;
> + Please refer to clock-bindings.txt.
> +- clock-names
> + Required elements: "hclock".
> +- #clock-cells
> + Should be 0.
> +- clock-output-names
> + Should contain the name of the clock driving the sensor master clock.
> +- pinctrl-names, pinctrl-0
> + Please refer to pinctrl-bindings.txt.
Required, but not in your example.
> +
> +
> +ISC supports a single port node with parallel bus. It should contain one
> +'port' child node with child 'endpoint' node. Please refer to the bindings
> +defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> +isc: isc@f0008000 {
> + compatible = "atmel,sama5d2-isc";
> + reg = <0xf0008000 0x4000>;
> + interrupts = <46 IRQ_TYPE_LEVEL_HIGH 5>;
> + clocks = <&isc_clk>, <&iscck>, <&isc_gclk>;
> + clock-names = "hclock";
> + #clock-cells = <0>;
> + clock-output-names = "isc-mck";
> +
> + port {
> + #address-cells = <1>;
> + #size-cells = <0>;
These 2 you can drop.
> +
> + isc_0: endpoint {
> + remote-endpoint = <&ov7740_0>;
> + hsync-active = <1>;
> + vsync-active = <0>;
> + pclk-sample = <1>;
> + };
> + };
> +
> +
> +};
> +
> +i2c1: i2c@fc028000 {
> + ov7740: camera@21 {
> + compatible = "ovti,ov7740";
Fix the indentation.
> + reg = <0x21>;
> +
> + clocks = <&isc>;
> + clock-names = "xvclk";
> + assigned-clocks = <&isc>;
> + assigned-clock-rates = <24000000>;
> +
> + port {
> + ov7740_0: endpoint {
> + remote-endpoint = <&isc_0>;
> + };
> + };
> +};
> --
> 2.7.4
>
[toc] | [prev] | [next] | [standalone]
| From | Boris Brezillon <boris.brezillon@free-electrons.com> |
|---|---|
| Date | 2016-06-09 00:00 +0200 |
| Subject | Re: [PATCH v4 2/2] [media] atmel-isc: DT binding for Image Sensor Controller driver |
| Message-ID | <rHTG1-5FE-15@gated-at.bofh.it> |
| In reply to | #1415787 |
On Tue, 7 Jun 2016 15:11:53 +0800
Songjun Wu <songjun.wu@atmel.com> wrote:
> DT binding documentation for ISC driver.
>
> Signed-off-by: Songjun Wu <songjun.wu@atmel.com>
> ---
>
> Changes in v4:
> - Remove the isc clock nodes.
>
> Changes in v3:
> - Remove the 'atmel,sensor-preferred'.
> - Modify the isc clock node according to the Rob's remarks.
>
> Changes in v2:
> - Remove the unit address of the endpoint.
> - Add the unit address to the clock node.
> - Avoid using underscores in node names.
> - Drop the "0x" in the unit address of the i2c node.
> - Modify the description of 'atmel,sensor-preferred'.
> - Add the description for the ISC internal clock.
>
> .../devicetree/bindings/media/atmel-isc.txt | 69 ++++++++++++++++++++++
> 1 file changed, 69 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/atmel-isc.txt
>
> diff --git a/Documentation/devicetree/bindings/media/atmel-isc.txt b/Documentation/devicetree/bindings/media/atmel-isc.txt
> new file mode 100644
> index 0000000..3f83524
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/atmel-isc.txt
> @@ -0,0 +1,69 @@
> +Atmel Image Sensor Controller (ISC)
> +----------------------------------------------
> +
> +Required properties for ISC:
> +- compatible
> + Must be "atmel,sama5d2-isc".
> +- reg
> + Physical base address and length of the registers set for the device.
> +- interrupts
> + Should contain IRQ line for the ISC.
> +- clocks
> + List of clock specifiers, corresponding to entries in
> + the clock-names property;
> + Please refer to clock-bindings.txt.
> +- clock-names
> + Required elements: "hclock".
> +- #clock-cells
> + Should be 0.
> +- clock-output-names
> + Should contain the name of the clock driving the sensor master clock.
> +- pinctrl-names, pinctrl-0
> + Please refer to pinctrl-bindings.txt.
> +
> +
> +ISC supports a single port node with parallel bus. It should contain one
> +'port' child node with child 'endpoint' node. Please refer to the bindings
> +defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> +isc: isc@f0008000 {
> + compatible = "atmel,sama5d2-isc";
> + reg = <0xf0008000 0x4000>;
> + interrupts = <46 IRQ_TYPE_LEVEL_HIGH 5>;
> + clocks = <&isc_clk>, <&iscck>, <&isc_gclk>;
> + clock-names = "hclock";
You have 3 clocks here and only one name. Are you sure this example is
actually working?
> + #clock-cells = <0>;
> + clock-output-names = "isc-mck";
> +
> + port {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + isc_0: endpoint {
> + remote-endpoint = <&ov7740_0>;
> + hsync-active = <1>;
> + vsync-active = <0>;
> + pclk-sample = <1>;
> + };
> + };
> +
> +
> +};
> +
> +i2c1: i2c@fc028000 {
> + ov7740: camera@21 {
> + compatible = "ovti,ov7740";
> + reg = <0x21>;
> +
> + clocks = <&isc>;
> + clock-names = "xvclk";
> + assigned-clocks = <&isc>;
> + assigned-clock-rates = <24000000>;
> +
> + port {
> + ov7740_0: endpoint {
> + remote-endpoint = <&isc_0>;
> + };
> + };
> +};
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [next] | [standalone]
| From | Boris Brezillon <boris.brezillon@free-electrons.com> |
|---|---|
| Date | 2016-06-08 23:40 +0200 |
| Subject | Re: [PATCH v4 1/2] [media] atmel-isc: add the Image Sensor Controller code |
| Message-ID | <rHTmF-5yr-11@gated-at.bofh.it> |
| In reply to | #1415786 |
Hi Songjun,
On Tue, 7 Jun 2016 15:11:52 +0800
Songjun Wu <songjun.wu@atmel.com> wrote:
> Add driver for the Image Sensor Controller. It manages
> incoming data from a parallel based CMOS/CCD sensor.
> It has an internal image processor, also integrates a
> triple channel direct memory access controller master
> interface.
>
> Signed-off-by: Songjun Wu <songjun.wu@atmel.com>
> ---
>
> Changes in v4:
> - Modify the isc clock code since the dt is changed.
>
> Changes in v3:
> - Add pm runtime feature.
> - Modify the isc clock code since the dt is changed.
>
> Changes in v2:
> - Add "depends on COMMON_CLK" and "VIDEO_V4L2_SUBDEV_API"
> in Kconfig file.
> - Correct typos and coding style according to Laurent's remarks
> - Delete the loop while in 'isc_clk_enable' function.
> - Add the code to support VIDIOC_CREATE_BUFS in
> 'isc_queue_setup' function.
> - Invoke isc_config to configure register in
> 'isc_start_streaming' function.
> - Add the struct completion 'comp' to synchronize with
> the frame end interrupt in 'isc_stop_streaming' function.
> - Check the return value of the clk_prepare_enable
> in 'isc_open' function.
> - Set the default format in 'isc_open' function.
> - Add an exit condition in the loop while in 'isc_config'.
> - Delete the hardware setup operation in 'isc_set_format'.
> - Refuse format modification during streaming
> in 'isc_s_fmt_vid_cap' function.
> - Invoke v4l2_subdev_alloc_pad_config to allocate and
> initialize the pad config in 'isc_async_complete' function.
> - Remove the '.owner = THIS_MODULE,' in atmel_isc_driver.
> - Replace the module_platform_driver_probe() with
> module_platform_driver().
>
> drivers/media/platform/Kconfig | 1 +
> drivers/media/platform/Makefile | 2 +
> drivers/media/platform/atmel/Kconfig | 9 +
> drivers/media/platform/atmel/Makefile | 1 +
> drivers/media/platform/atmel/atmel-isc-regs.h | 276 +++++
> drivers/media/platform/atmel/atmel-isc.c | 1580 +++++++++++++++++++++++++
> 6 files changed, 1869 insertions(+)
> create mode 100644 drivers/media/platform/atmel/Kconfig
> create mode 100644 drivers/media/platform/atmel/Makefile
> create mode 100644 drivers/media/platform/atmel/atmel-isc-regs.h
> create mode 100644 drivers/media/platform/atmel/atmel-isc.c
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 84e041c..91d7aea 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -110,6 +110,7 @@ source "drivers/media/platform/exynos4-is/Kconfig"
> source "drivers/media/platform/s5p-tv/Kconfig"
> source "drivers/media/platform/am437x/Kconfig"
> source "drivers/media/platform/xilinx/Kconfig"
> +source "drivers/media/platform/atmel/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 bbb7bd1..ad8f471 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -55,4 +55,6 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/
>
> obj-$(CONFIG_VIDEO_XILINX) += xilinx/
>
> +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
> +
> ccflags-y += -I$(srctree)/drivers/media/i2c
> diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig
> new file mode 100644
> index 0000000..867dca2
> --- /dev/null
> +++ b/drivers/media/platform/atmel/Kconfig
> @@ -0,0 +1,9 @@
> +config VIDEO_ATMEL_ISC
> + tristate "ATMEL Image Sensor Controller (ISC) support"
> + depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA
> + depends on ARCH_AT91 || COMPILE_TEST
> + select VIDEOBUF2_DMA_CONTIG
> + select REGMAP_MMIO
> + help
> + This module makes the ATMEL Image Sensor Controller available
> + as a v4l2 device.
> \ No newline at end of file
> diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile
> new file mode 100644
> index 0000000..9d7c999
> --- /dev/null
> +++ b/drivers/media/platform/atmel/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o
> diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
> new file mode 100644
> index 0000000..dda9396
> --- /dev/null
> +++ b/drivers/media/platform/atmel/atmel-isc-regs.h
> @@ -0,0 +1,276 @@
> +#ifndef __ATMEL_ISC_REGS_H
> +#define __ATMEL_ISC_REGS_H
> +
> +#include <linux/bitops.h>
> +
> +/* ISC Control Enable Register 0 */
> +#define ISC_CTRLEN 0x00000000
> +
> +#define ISC_CTRLEN_CAPTURE BIT(0)
> +#define ISC_CTRLEN_CAPTURE_MASK BIT(0)
> +
> +#define ISC_CTRLEN_UPPRO BIT(1)
> +#define ISC_CTRLEN_UPPRO_MASK BIT(1)
> +
> +#define ISC_CTRLEN_HISREQ BIT(2)
> +#define ISC_CTRLEN_HISREQ_MASK BIT(2)
> +
> +#define ISC_CTRLEN_HISCLR BIT(3)
> +#define ISC_CTRLEN_HISCLR_MASK BIT(3)
> +
> +/* ISC Control Disable Register 0 */
> +#define ISC_CTRLDIS 0x00000004
> +
> +#define ISC_CTRLDIS_DISABLE BIT(0)
> +#define ISC_CTRLDIS_DISABLE_MASK BIT(0)
> +
> +#define ISC_CTRLDIS_SWRST BIT(8)
> +#define ISC_CTRLDIS_SWRST_MASK BIT(8)
No need to create _MASK macros when you manipulate single bits.
> +
> +/* ISC Control Status Register 0 */
> +#define ISC_CTRLSR 0x00000008
> +
> +#define ISC_CTRLSR_CAPTURE BIT(0)
> +#define ISC_CTRLSR_UPPRO BIT(1)
> +#define ISC_CTRLSR_HISREQ BIT(2)
> +#define ISC_CTRLSR_FIELD BIT(4)
> +#define ISC_CTRLSR_SIP BIT(31)
The CTREN and CTRLSR seem similar, please share the same definition.
Something like:
ISC_CTRL_CAPTURE BIT(0)
ISC_CTRL_UPPRO BIT(1)
...
> +
> +/* ISC Parallel Front End Configuration 0 Register */
> +#define ISC_PFE_CFG0 0x0000000c
> +
> +#define ISC_PFE_CFG0_HPOL_LOW BIT(0)
> +#define ISC_PFE_CFG0_HPOL_HIGH 0x0
> +#define ISC_PFE_CFG0_HPOL_MASK BIT(0)
> +
> +#define ISC_PFE_CFG0_VPOL_LOW BIT(1)
> +#define ISC_PFE_CFG0_VPOL_HIGH 0x0
> +#define ISC_PFE_CFG0_VPOL_MASK BIT(1)
> +
> +#define ISC_PFE_CFG0_PPOL_LOW BIT(2)
> +#define ISC_PFE_CFG0_PPOL_HIGH 0x0
> +#define ISC_PFE_CFG0_PPOL_MASK BIT(2)
Ditto. Drop the _MASK and _HIGH definitions and keep the _LOW ones.
> +
> +#define ISC_PFE_CFG0_MODE_PROGRESSIVE 0x0
Nit: the value is 0 here, so it works fine, but it would be clearer to
put (0 << 4), since the mode field starts at bit4.
> +#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4)
> +
> +#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28)
> +#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28)
> +#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28)
> +#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28)
> +#define ISC_PFG_CFG0_BPS_TWELVE 0x0
Ditto.
> +#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28)
> +
> +/* ISC Clock Enable Register */
> +#define ISC_CLKEN 0x00000018
> +#define ISC_CLKEN_EN 0x1
> +#define ISC_CLKEN_EN_SHIFT(n) (n)
> +#define ISC_CLKEN_EN_MASK(n) BIT(n)
> +
> +/* ISC Clock Disable Register */
> +#define ISC_CLKDIS 0x0000001c
> +#define ISC_CLKDIS_DIS 0x1
> +#define ISC_CLKDIS_DIS_SHIFT(n) (n)
> +#define ISC_CLKDIS_DIS_MASK(n) BIT(n)
Same remark as above, just define a single macro
ISC_CLK(n) BIT(n)
and use it for the EN/DIS/STA registers.
No need for the _MASK() and _SHIFT() ones.
> +
> +/* ISC Clock Status Register */
> +#define ISC_CLKSR 0x00000020
> +#define ISC_CLKSR_CLK_MASK(n) BIT(n)
> +#define ISC_CLKSR_SIP_PROGRESS BIT(31)
> +
> +/* ISC Clock Configuration Register */
> +#define ISC_CLKCFG 0x00000024
> +#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16)
> +#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16)
> +#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8)
> +#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8))
> +
> +/* ISC Interrupt Enable Register */
> +#define ISC_INTEN 0x00000028
> +
> +#define ISC_INTEN_DDONE BIT(8)
> +#define ISC_INTEN_DDONE_MASK BIT(8)
> +
> +/* ISC Interrupt Disable Register */
> +#define ISC_INTDIS 0x0000002c
> +
> +#define ISC_INTDIS_DDONE BIT(8)
> +#define ISC_INTDIS_DDONE_MASK BIT(8)
Ditto.
> +
> +/* ISC Interrupt Mask Register */
> +#define ISC_INTMASK 0x00000030
> +
> +/* ISC Interrupt Status Register */
> +#define ISC_INTSR 0x00000034
> +
> +#define ISC_INTSR_DDONE BIT(8)
> +
> +/* ISC White Balance Control Register */
> +#define ISC_WB_CTRL 0x00000058
> +
> +#define ISC_WB_CTRL_EN BIT(0)
> +#define ISC_WB_CTRL_DIS 0x0
> +#define ISC_WB_CTRL_MASK BIT(0)
Ditto.
> +
> +/* ISC White Balance Configuration Register */
> +#define ISC_WB_CFG 0x0000005c
> +
> +#define ISC_WB_CFG_BAYCFG_GRGR 0x0
> +#define ISC_WB_CFG_BAYCFG_RGRG 0x1
> +#define ISC_WB_CFG_BAYCFG_GBGB 0x2
> +#define ISC_WB_CFG_BAYCFG_BGBG 0x3
> +#define ISC_WB_CFG_BAYCFG_MASK GENMASK(1, 0)
> +
> +/* ISC Color Filter Array Control Register */
> +#define ISC_CFA_CTRL 0x00000070
> +
> +#define ISC_CFA_CTRL_EN BIT(0)
> +#define ISC_CFA_CTRL_DIS 0x0
> +#define ISC_CFA_CTRL_MASK BIT(0)
> +
> +/* ISC Color Filter Array Configuration Register */
> +#define ISC_CFA_CFG 0x00000074
> +
> +#define ISC_CFA_CFG_BAY_GRGR 0x0
> +#define ISC_CFA_CFG_BAY_RGRG 0x1
> +#define ISC_CFA_CFG_BAY_GBGB 0x2
> +#define ISC_CFA_CFG_BAY_BGBG 0x3
> +#define ISC_CFA_CFG_BAY_MASK GENMASK(1, 0)
Again, same values for WB and CFA => merge the definitions.
> +
> +/* ISC Color Correction Control Register */
> +#define ISC_CC_CTRL 0x00000078
> +
> +#define ISC_CC_CTRL_EN BIT(0)
> +#define ISC_CC_CTRL_DIS 0x0
> +#define ISC_CC_CTRL_MASK BIT(0)
> +
> +/* ISC Gamma Correction Control Register */
> +#define ISC_GAM_CTRL 0x00000094
> +
> +#define ISC_GAM_CTRL_EN BIT(0)
> +#define ISC_GAM_CTRL_DIS 0x0
> +#define ISC_GAM_CTRL_MASK BIT(0)
> +
> +#define ISC_GAM_CTRL_B_EN BIT(1)
> +#define ISC_GAM_CTRL_B_DIS 0x0
> +#define ISC_GAM_CTRL_B_MASK BIT(1)
> +
> +#define ISC_GAM_CTRL_G_EN BIT(2)
> +#define ISC_GAM_CTRL_G_DIS 0x0
> +#define ISC_GAM_CTRL_G_MASK BIT(2)
> +
> +#define ISC_GAM_CTRL_R_EN BIT(3)
> +#define ISC_GAM_CTRL_R_DIS 0x0
> +#define ISC_GAM_CTRL_R_MASK BIT(3)
> +
> +#define ISC_GAM_CTRL_ALL_CHAN_MASK (ISC_GAM_CTRL_B_MASK | \
> + ISC_GAM_CTRL_G_MASK | \
> + ISC_GAM_CTRL_R_MASK)
> +
> +/* Color Space Conversion Control Register */
> +#define ISC_CSC_CTRL 0x00000398
> +
> +#define ISC_CSC_CTRL_EN BIT(0)
> +#define ISC_CSC_CTRL_DIS 0x0
> +#define ISC_CSC_CTRL_MASK BIT(0)
> +
> +/* Contrast And Brightness Control Register */
> +#define ISC_CBC_CTRL 0x000003b4
> +
> +#define ISC_CBC_CTRL_EN BIT(0)
> +#define ISC_CBC_CTRL_DIS 0x0
> +#define ISC_CBC_CTRL_MASK BIT(0)
> +
> +/* Subsampling 4:4:4 to 4:2:2 Control Register */
> +#define ISC_SUB422_CTRL 0x000003c4
> +
> +#define ISC_SUB422_CTRL_EN BIT(0)
> +#define ISC_SUB422_CTRL_DIS 0x0
> +#define ISC_SUB422_CTRL_MASK BIT(0)
> +
> +/* Subsampling 4:2:2 to 4:2:0 Control Register */
> +#define ISC_SUB420_CTRL 0x000003cc
> +
> +#define ISC_SUB420_CTRL_EN BIT(0)
> +#define ISC_SUB420_CTRL_DIS 0x0
> +#define ISC_SUB420_CTRL_MASK BIT(0)
> +
> +#define ISC_SUB420_CTRL_FILTER_PROG 0x0
> +#define ISC_SUB420_CTRL_FILTER_INTER BIT(4)
> +#define ISC_SUB420_CTRL_FILTER_MASK BIT(4)
> +
> +/* Rounding, Limiting and Packing Configuration Register */
> +#define ISC_RLP_CFG 0x000003d0
> +
> +#define ISC_RLP_CFG_MODE_DAT8 0x0
> +#define ISC_RLP_CFG_MODE_DAT9 0x1
> +#define ISC_RLP_CFG_MODE_DAT10 0x2
> +#define ISC_RLP_CFG_MODE_DAT11 0x3
> +#define ISC_RLP_CFG_MODE_DAT12 0x4
> +#define ISC_RLP_CFG_MODE_DATY8 0x5
> +#define ISC_RLP_CFG_MODE_DATY10 0x6
> +#define ISC_RLP_CFG_MODE_ARGB444 0x7
> +#define ISC_RLP_CFG_MODE_ARGB555 0x8
> +#define ISC_RLP_CFG_MODE_RGB565 0x9
> +#define ISC_RLP_CFG_MODE_ARGB32 0xa
> +#define ISC_RLP_CFG_MODE_YYCC 0xb
> +#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc
> +#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0)
> +
> +/* DMA Configuration Register */
> +#define ISC_DCFG 0x000003e0
> +#define ISC_DCFG_IMODE_PACKED8 0x0
> +#define ISC_DCFG_IMODE_PACKED16 0x1
> +#define ISC_DCFG_IMODE_PACKED32 0x2
> +#define ISC_DCFG_IMODE_YC422SP 0x3
> +#define ISC_DCFG_IMODE_YC422P 0x4
> +#define ISC_DCFG_IMODE_YC420SP 0x5
> +#define ISC_DCFG_IMODE_YC420P 0x6
> +#define ISC_DCFG_IMODE_MASK GENMASK(2, 0)
> +
> +#define ISC_DCFG_YMBSIZE_SINGLE 0x0
> +#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4)
> +#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4)
> +#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4)
> +#define ISC_DCFG_YMBSIZE_MASK GENMASK(5, 4)
> +
> +#define ISC_DCFG_CMBSIZE_SINGLE 0x0
> +#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8)
> +#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8)
> +#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8)
> +#define ISC_DCFG_CMBSIZE_MASK GENMASK(9, 8)
> +
> +/* DMA Control Register */
> +#define ISC_DCTRL 0x000003e4
> +
> +#define ISC_DCTRL_DE_EN BIT(0)
> +#define ISC_DCTRL_DE_DIS 0x0
> +#define ISC_DCTRL_DE_MASK BIT(0)
> +
> +#define ISC_DCTRL_DVIEW_PACKED 0x0
> +#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1)
> +#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1)
> +#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1)
> +
> +#define ISC_DCTRL_IE_IS 0x0
> +#define ISC_DCTRL_IE_NOT BIT(4)
> +#define ISC_DCTRL_IE_MASK BIT(4)
> +
> +#define ISC_DCTRL_WB_EN BIT(5)
> +#define ISC_DCTRL_WB_DIS 0x0
> +#define ISC_DCTRL_WB_MASK BIT(5)
> +
> +#define ISC_DCTRL_DESC_IS_DONE BIT(7)
> +#define ISC_DCTRL_DESC_NOT_DONE 0x0
> +#define ISC_DCTRL_DESC_MASK BIT(7)
> +
> +/* DMA Descriptor Address Register */
> +#define ISC_DNDA 0x000003e8
> +
> +/* DMA Address 0 Register */
> +#define ISC_DAD0 0x000003ec
> +
> +/* DMA Stride 0 Register */
> +#define ISC_DST0 0x000003f0
I just didn't go through all the definitions, but please make sure you
get rid of all the duplicate and useless definitions in your next
version.
> +
> +#endif
> diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
> new file mode 100644
> index 0000000..57f3a97
> --- /dev/null
> +++ b/drivers/media/platform/atmel/atmel-isc.c
> @@ -0,0 +1,1580 @@
> +/*
> + * Atmel Image Sensor Controller (ISC) driver
> + *
> + * Copyright (C) 2016 Atmel
> + *
> + * Author: Songjun Wu <songjun.wu@atmel.com>
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
> + *
> + * ISC video pipeline integrates the following submodules:
> + * PFE: Parallel Front End to sample the camera sensor input stream
> + * WB: Programmable white balance in the Bayer domain
> + * CFA: Color filter array interpolation module
> + * CC: Programmable color correction
> + * GAM: Gamma correction
> + * CSC: Programmable color space conversion
> + * CBC: Contrast and Brightness control
> + * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
> + * RLP: This module performs rounding, range limiting
> + * and packing of the incoming data
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-image-sizes.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-of.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "atmel-isc-regs.h"
> +
> +#define ATMEL_ISC_NAME "atmel_isc"
> +
> +#define ISC_MAX_SUPPORT_WIDTH 2592
> +#define ISC_MAX_SUPPORT_HEIGHT 1944
> +
> +#define ISC_CLK_MAX_DIV 255
> +
> +enum isc_clk_id {
> + ISC_ISPCK = 0,
> + ISC_MCK = 1,
> +};
> +
> +struct isc_clk {
> + struct clk_hw hw;
> + struct clk *clk;
Why do you need this backpointer?
> + struct regmap *regmap;
> + spinlock_t *lock;
I'm pretty you don't need this lock. The regmap is taking care of
concurrent accesses for you.
> + u8 id;
> + u8 parent_id;
Hm, do we really want to cache clk source configuration? What's the
problem with setting it directly when the CCF calls ->set_parent()?
> + u32 div;
Ditto.
We usually use this caching mechanism when we can't decorrelate
enable/disable operation from parenting/dividing operations, but that
doesn't seem to be the case here.
> +
> + struct isc_device *isc;
Do you really need this pointer to the isc device?
> +};
> +
> +#define to_isc_clk(hw) container_of(hw, struct isc_clk, hw)
> +
> +struct isc_buffer {
> + struct vb2_v4l2_buffer vb;
> + struct list_head list;
> +};
> +
> +struct isc_subdev_entity {
> + struct v4l2_subdev *sd;
> + struct v4l2_async_subdev *asd;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev_pad_config *config;
> +
> + u32 pfe_cfg0;
> +
> + struct list_head list;
> +};
> +
> +/*
> + * struct isc_format - ISC media bus format information
> + * @fourcc: Fourcc code for this format
> + * @isc_mbus_code: V4L2 media bus format code if ISC is preferred
> + * @sd_mbus_code: V4L2 media bus format code if subdev is preferred
> + * @bpp: Bytes per pixel (when stored in memory)
> + * @reg_sd_bps: reg value for bits per sample if subdev is preferred
> + * (when transferred over a bus)
> + * @reg_isc_bps: reg value for bits per sample if ISC is preferred
> + * (when transferred over a bus)
> + * @pipeline: pipeline switch if ISC is preferred
> + * @isc_support: ISC can convert raw format to this format
> + * @sd_support: Subdev supports this format
> + */
> +struct isc_format {
> + u32 fourcc;
> + u32 isc_mbus_code;
> + u32 sd_mbus_code;
> +
> + u8 bpp;
> +
> + u32 reg_sd_bps;
> + u32 reg_isc_bps;
> +
> + u32 reg_wb_cfg;
> + u32 reg_cfa_cfg;
> + u32 reg_rlp_mode;
> + u32 reg_dcfg_imode;
> + u32 reg_dctrl_dview;
> +
> + u32 pipeline;
> +
> + bool isc_support;
> + bool sd_support;
> +};
> +
> +struct isc_device {
> + struct regmap *regmap;
> + struct clk *hclock;
> + struct clk *ispck;
> + struct isc_clk isc_clks[2];
> + spinlock_t clk_lock;
Let the regmap use its own lock instead of passing yours.
> +
> + struct device *dev;
> + struct v4l2_device v4l2_dev;
> + struct video_device video_dev;
> +
> + struct vb2_queue vb2_vidq;
> + struct vb2_alloc_ctx *alloc_ctx;
> +
> + spinlock_t dma_queue_lock;
> + struct list_head dma_queue;
> + struct isc_buffer *cur_frm;
> + unsigned int sequence;
> + bool stop;
> + struct completion comp;
> +
> + struct v4l2_format fmt;
> +
> + struct isc_format **user_formats;
> + unsigned int num_user_formats;
> + const struct isc_format *current_fmt;
> +
> + struct mutex lock;
> +
> + struct isc_subdev_entity *current_subdev;
> + struct list_head subdev_entities;
> +};
> +
> +struct reg_mask {
> + u32 reg;
> + u32 mask;
> +};
You're reinventing the wheel, please use reg_field.
> +
> +static unsigned int sensor_preferred = 1;
> +module_param(sensor_preferred, uint, S_IRUGO|S_IWUSR);
> +MODULE_PARM_DESC(sensor_preferred,
> +"Sensor is preferred to output the specified format (1-on 0-off) default 1");
Fix the indentation.
> +
> +/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
> +const struct reg_mask pipeline_regs[] = {
> + { ISC_WB_CTRL, ISC_WB_CTRL_MASK },
> + { ISC_CFA_CTRL, ISC_CFA_CTRL_MASK },
> + { ISC_CC_CTRL, ISC_CC_CTRL_MASK },
> + { ISC_GAM_CTRL, ISC_GAM_CTRL_MASK | ISC_GAM_CTRL_ALL_CHAN_MASK },
> + { ISC_CSC_CTRL, ISC_CSC_CTRL_MASK },
> + { ISC_CBC_CTRL, ISC_CBC_CTRL_MASK },
> + { ISC_SUB422_CTRL, ISC_SUB422_CTRL_MASK },
> + { ISC_SUB420_CTRL, ISC_SUB420_CTRL_MASK }
> +};
> +
> +#define RAW_FMT_INDEX_START 0
> +#define RAW_FMT_INDEX_END 11
> +#define ISC_FMT_INDEX_START 12
> +#define ISC_FMT_INDEX_END 12
> +
> +/*
> + * index(0~11): raw formats.
> + * index(12~12): the formats which can be converted from raw format by ISC.
> + * index(): the formats which can only be provided by subdev.
> + */
> +static struct isc_format isc_formats[] = {
> +{V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8,
> +1, ISC_PFE_CFG0_BPS_EIGHT, ISC_PFE_CFG0_BPS_EIGHT, ISC_WB_CFG_BAYCFG_BGBG,
> +ISC_CFA_CFG_BAY_BGBG, ISC_RLP_CFG_MODE_DAT8, ISC_DCFG_IMODE_PACKED8,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
> +1, ISC_PFE_CFG0_BPS_EIGHT, ISC_PFE_CFG0_BPS_EIGHT, ISC_WB_CFG_BAYCFG_GBGB,
> +ISC_CFA_CFG_BAY_GBGB, ISC_RLP_CFG_MODE_DAT8, ISC_DCFG_IMODE_PACKED8,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8,
> +1, ISC_PFE_CFG0_BPS_EIGHT, ISC_PFE_CFG0_BPS_EIGHT, ISC_WB_CFG_BAYCFG_GRGR,
> +ISC_CFA_CFG_BAY_GRGR, ISC_RLP_CFG_MODE_DAT8, ISC_DCFG_IMODE_PACKED8,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
> +1, ISC_PFE_CFG0_BPS_EIGHT, ISC_PFE_CFG0_BPS_EIGHT, ISC_WB_CFG_BAYCFG_RGRG,
> +ISC_CFA_CFG_BAY_RGRG, ISC_RLP_CFG_MODE_DAT8, ISC_DCFG_IMODE_PACKED8,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +
> +{V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10,
> +2, ISC_PFG_CFG0_BPS_TEN, ISC_PFG_CFG0_BPS_TEN, ISC_WB_CFG_BAYCFG_BGBG,
> +ISC_CFA_CFG_BAY_BGBG, ISC_RLP_CFG_MODE_DAT10, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
> +2, ISC_PFG_CFG0_BPS_TEN, ISC_PFG_CFG0_BPS_TEN, ISC_WB_CFG_BAYCFG_GBGB,
> +ISC_CFA_CFG_BAY_GBGB, ISC_RLP_CFG_MODE_DAT10, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10,
> +2, ISC_PFG_CFG0_BPS_TEN, ISC_PFG_CFG0_BPS_TEN, ISC_WB_CFG_BAYCFG_GRGR,
> +ISC_CFA_CFG_BAY_GRGR, ISC_RLP_CFG_MODE_DAT10, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
> +2, ISC_PFG_CFG0_BPS_TEN, ISC_PFG_CFG0_BPS_TEN, ISC_WB_CFG_BAYCFG_RGRG,
> +ISC_CFA_CFG_BAY_RGRG, ISC_RLP_CFG_MODE_DAT10, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +
> +{V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR12_1X12,
> +2, ISC_PFG_CFG0_BPS_TWELVE, ISC_PFG_CFG0_BPS_TWELVE, ISC_WB_CFG_BAYCFG_BGBG,
> +ISC_CFA_CFG_BAY_BGBG, ISC_RLP_CFG_MODE_DAT12, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12,
> +2, ISC_PFG_CFG0_BPS_TWELVE, ISC_PFG_CFG0_BPS_TWELVE, ISC_WB_CFG_BAYCFG_GBGB,
> +ISC_CFA_CFG_BAY_GBGB, ISC_RLP_CFG_MODE_DAT12, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12,
> +2, ISC_PFG_CFG0_BPS_TWELVE, ISC_PFG_CFG0_BPS_TWELVE, ISC_WB_CFG_BAYCFG_GRGR,
> +ISC_CFA_CFG_BAY_GRGR, ISC_RLP_CFG_MODE_DAT12, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +{V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12,
> +2, ISC_PFG_CFG0_BPS_TWELVE, ISC_PFG_CFG0_BPS_TWELVE, ISC_WB_CFG_BAYCFG_RGRG,
> +ISC_CFA_CFG_BAY_RGRG, ISC_RLP_CFG_MODE_DAT12, ISC_DCFG_IMODE_PACKED16,
> +ISC_DCTRL_DVIEW_PACKED, 0x0, false, false},
> +
> +{V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8,
> +2, ISC_PFE_CFG0_BPS_EIGHT, ISC_PFE_CFG0_BPS_EIGHT, ISC_WB_CFG_BAYCFG_BGBG,
> +ISC_CFA_CFG_BAY_BGBG, ISC_RLP_CFG_MODE_DAT8, ISC_DCFG_IMODE_PACKED8,
> +ISC_DCTRL_DVIEW_PACKED, 0x7f, false, false},
> +};
Indentation.
> +
> +static int isc_clk_enable(struct clk_hw *hw)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> + u32 id = isc_clk->id;
> + struct regmap *regmap = isc_clk->regmap;
> + unsigned long flags;
> +
> + dev_dbg(isc_clk->isc->dev, "ISC CLK: %s, div = %d, parent id = %d\n",
> + __func__, isc_clk->div, isc_clk->parent_id);
> +
> + spin_lock_irqsave(isc_clk->lock, flags);
Drop the locking here...
> +
> + regmap_update_bits(regmap, ISC_CLKCFG,
> + ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
> + (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
> + (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
... this update bit operation is already protected.
> +
> + regmap_update_bits(regmap, ISC_CLKEN,
> + ISC_CLKEN_EN_MASK(id),
> + ISC_CLKEN_EN << ISC_CLKEN_EN_SHIFT(id));
You're manipulating a write-only register, use
regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
> +
> + spin_unlock_irqrestore(isc_clk->lock, flags);
> +
> + return 0;
> +}
> +
> +static void isc_clk_disable(struct clk_hw *hw)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> + u32 id = isc_clk->id;
> + unsigned long flags;
> +
> + spin_lock_irqsave(isc_clk->lock, flags);
> +
> + regmap_update_bits(isc_clk->regmap, ISC_CLKDIS,
> + ISC_CLKDIS_DIS_MASK(id),
> + ISC_CLKDIS_DIS << ISC_CLKDIS_DIS_SHIFT(id));
Ditto (drop the lock and use regmap_write()).
> +
> + spin_unlock_irqrestore(isc_clk->lock, flags);
> +}
> +
> +static int isc_clk_is_enabled(struct clk_hw *hw)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> + unsigned long flags;
> + u32 status;
> +
> + spin_lock_irqsave(isc_clk->lock, flags);
> + regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
> + spin_unlock_irqrestore(isc_clk->lock, flags);
Ditto (drop the lock).
> +
> + return status & ISC_CLKSR_CLK_MASK(isc_clk->id) ? 1 : 0;
> +}
> +
> +static unsigned long
> +isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> +
> + return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
> +}
> +
> +static int isc_clk_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> + long best_rate = -EINVAL;
> + int best_diff = -1;
> + unsigned int i, div;
> +
> + for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
> + struct clk_hw *parent;
> + unsigned long parent_rate;
> +
> + parent = clk_hw_get_parent_by_index(hw, i);
> + if (!parent)
> + continue;
> +
> + parent_rate = clk_hw_get_rate(parent);
> + if (!parent_rate)
> + continue;
> +
> + for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
> + unsigned long rate;
> + int diff;
> +
> + rate = DIV_ROUND_CLOSEST(parent_rate, div);
> + diff = abs(req->rate - rate);
> +
> + if (best_diff < 0 || best_diff > diff) {
> + best_rate = rate;
> + best_diff = diff;
> + req->best_parent_rate = parent_rate;
> + req->best_parent_hw = parent;
> + }
> +
> + if (!best_diff || rate < req->rate)
> + break;
> + }
> +
> + if (!best_diff)
> + break;
> + }
> +
> + dev_dbg(isc_clk->isc->dev,
Ok, that explains why you were keeping a pointer to isc. Just store a
pointer to dev and you should be fine.
> + "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
> + __func__, best_rate,
> + __clk_get_name((req->best_parent_hw)->clk),
> + req->best_parent_rate);
> +
> + if (best_rate < 0)
> + return best_rate;
> +
> + req->rate = best_rate;
> +
> + return 0;
> +}
> +
> +static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> +
> + if (index >= clk_hw_get_num_parents(hw))
> + return -EINVAL;
> +
> + isc_clk->parent_id = index;
Apply the value directly in the CLKCFG register. If you want to make
sure div and source are applied at the same time, then implement
->set_rate_and_parent() instead of implementing both ->set_rate() and
->set_parent().
> +
> + return 0;
> +}
> +
> +static u8 isc_clk_get_parent(struct clk_hw *hw)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> +
> + return isc_clk->parent_id;
Read the register value.
> +}
> +
> +static int isc_clk_set_rate(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct isc_clk *isc_clk = to_isc_clk(hw);
> + u32 div;
> +
> + if (!rate)
> + return -EINVAL;
> +
> + div = DIV_ROUND_CLOSEST(parent_rate, rate);
> + if (div > (ISC_CLK_MAX_DIV + 1) || !div)
> + return -EINVAL;
> +
> + isc_clk->div = div - 1;
> +
> + return 0;
> +}
See my comment about ->set_rate_and_parent().
> +
> +static const struct clk_ops isc_clk_ops = {
> + .enable = isc_clk_enable,
> + .disable = isc_clk_disable,
> + .is_enabled = isc_clk_is_enabled,
> + .recalc_rate = isc_clk_recalc_rate,
> + .determine_rate = isc_clk_determine_rate,
> + .set_parent = isc_clk_set_parent,
> + .get_parent = isc_clk_get_parent,
> + .set_rate = isc_clk_set_rate,
> +};
> +
> +static int isc_clk_register(struct isc_device *isc, unsigned int id)
> +{
> + struct regmap *regmap = isc->regmap;
> + struct device_node *np = isc->dev->of_node;
> + struct isc_clk *isc_clk;
> + struct clk_init_data init;
> + const char *clk_name = np->name;
> + const char **parent_names;
> + int num_parents, ret = 0;
> +
> + num_parents = of_clk_get_parent_count(np);
> + if (num_parents < 1 || num_parents > 3)
> + return -EINVAL;
> +
> + if (num_parents > 2 && id == ISC_ISPCK)
> + num_parents = 2;
> +
> + parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL);
> + if (!parent_names)
> + return -ENOMEM;
You have a maximum of 3 parents, you should probably declare a static
array:
const char *parent_names[3];
> +
> + of_clk_parent_fill(np, parent_names, num_parents);
> +
> + if (id == ISC_MCK)
> + of_property_read_string(np, "clock-output-names", &clk_name);
> + else
> + clk_name = "isc-ispck";
Hm, that's a bit weird, but I guess you have no other choice, since
this clock is only used internally.
> +
> + init.parent_names = parent_names;
> + init.num_parents = num_parents;
> + init.name = clk_name;
> + init.ops = &isc_clk_ops;
> + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
> +
> + isc_clk = &isc->isc_clks[id];
> + isc_clk->hw.init = &init;
> + isc_clk->regmap = regmap;
> + isc_clk->lock = &isc->clk_lock;
> + isc_clk->id = id;
> + isc_clk->isc = isc;
> +
> + isc_clk->clk = clk_register(NULL, &isc_clk->hw);
clk_register(isc->dev, &isc_clk->hw);
> + if (IS_ERR(isc_clk->clk)) {
> + dev_err(isc->dev, "%s: clock register fail\n", clk_name);
> + ret = PTR_ERR(isc_clk->clk);
> + goto free_parent_names;
> + } else if (id == ISC_MCK)
> + of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
> +
> +free_parent_names:
> + kfree(parent_names);
> + return ret;
> +}
> +
> +static int isc_clk_init(struct isc_device *isc)
> +{
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
> + isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
> +
> + spin_lock_init(&isc->clk_lock);
> +
> + for (i = 0; i < 2; i++) {
> + ret = isc_clk_register(isc, i);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void isc_clk_cleanup(struct isc_device *isc)
> +{
> + unsigned int i;
> +
> + of_clk_del_provider(isc->dev->of_node);
> +
> + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
> + struct isc_clk *isc_clk = &isc->isc_clks[i];
> +
> + if (!IS_ERR(isc_clk->clk))
> + clk_unregister(isc_clk->clk);
> + }
> +}
> +
> +static int isc_queue_setup(struct vb2_queue *vq,
> + unsigned int *nbuffers, unsigned int *nplanes,
> + unsigned int sizes[], void *alloc_ctxs[])
Try to align parameters to the open parenthesis.
> +{
> + struct isc_device *isc = vb2_get_drv_priv(vq);
> + unsigned int size = isc->fmt.fmt.pix.sizeimage;
> +
> + alloc_ctxs[0] = isc->alloc_ctx;
> +
> + if (*nplanes)
> + return sizes[0] < size ? -EINVAL : 0;
> +
> + *nplanes = 1;
> + sizes[0] = size;
> +
> + return 0;
> +}
> +
> +static int isc_buffer_prepare(struct vb2_buffer *vb)
> +{
> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
> + unsigned long size = isc->fmt.fmt.pix.sizeimage;
> +
> + if (vb2_plane_size(vb, 0) < size) {
> + v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n",
> + vb2_plane_size(vb, 0), size);
> + return -EINVAL;
> + }
> +
> + vb2_set_plane_payload(vb, 0, size);
> +
> + vbuf->field = isc->fmt.fmt.pix.field;
> +
> + return 0;
> +}
> +
> +static inline void isc_start_dma(struct regmap *regmap,
> + struct isc_buffer *frm, u32 dview)
> +{
> + dma_addr_t addr;
> +
> + addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
> +
> + regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
> + regmap_write(regmap, ISC_DAD0, addr);
> + regmap_update_bits(regmap, ISC_CTRLEN,
> + ISC_CTRLEN_CAPTURE_MASK, ISC_CTRLEN_CAPTURE);
> +}
> +
> +static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
> +{
> + if ((sensor_preferred && isc_fmt->sd_support) ||
> + !isc_fmt->isc_support)
> + return true;
> + else
> + return false;
> +}
> +
> +static void isc_set_pipeline(struct regmap *regmap, u32 pipeline)
> +{
> + const struct reg_mask *reg = &pipeline_regs[0];
> + u32 val;
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(pipeline_regs); i++) {
> + if (pipeline & BIT(i))
> + val = reg->mask;
> + else
> + val = 0;
> +
> + regmap_update_bits(regmap, reg->reg, reg->mask, val);
regmap_field_update_bits();
> +
> + reg++;
> + }
> +}
It's a long driver, so I'll leave the remaining bits for someone else,
but I guess you got the idea on the different aspects I pointed out,
and you'll be able to adjust the rest of the code accordingly ;).
Regards,
Boris
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web