diff options
Diffstat (limited to 'drivers/media/i2c')
44 files changed, 8266 insertions, 1762 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 226454b6a90d..74ff833ff48c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -25,8 +25,15 @@ config VIDEO_IR_I2C # V4L2 I2C drivers that are related with Camera support # -menu "Camera sensor devices" - visible if MEDIA_CAMERA_SUPPORT +menuconfig VIDEO_CAMERA_SENSOR + bool "Camera sensor devices" + depends on MEDIA_CAMERA_SUPPORT && I2C + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + default y + +if VIDEO_CAMERA_SENSOR config VIDEO_APTINA_PLL tristate @@ -36,10 +43,6 @@ config VIDEO_CCS_PLL config VIDEO_AR0521 tristate "ON Semiconductor AR0521 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the ON Semiconductor AR0521 camera. @@ -49,10 +52,6 @@ config VIDEO_AR0521 config VIDEO_HI556 tristate "Hynix Hi-556 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-556 camera. @@ -62,10 +61,6 @@ config VIDEO_HI556 config VIDEO_HI846 tristate "Hynix Hi-846 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-846 camera. @@ -75,10 +70,6 @@ config VIDEO_HI846 config VIDEO_HI847 tristate "Hynix Hi-847 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Hynix Hi-847 camera. @@ -88,10 +79,6 @@ config VIDEO_HI847 config VIDEO_IMX208 tristate "Sony IMX208 sensor support" - depends on I2C && VIDEO_DEV - depends on MEDIA_CAMERA_SUPPORT - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX208 camera. @@ -101,10 +88,7 @@ config VIDEO_IMX208 config VIDEO_IMX214 tristate "Sony IMX214 sensor support" - depends on GPIOLIB && I2C && VIDEO_DEV - select V4L2_FWNODE - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API + depends on GPIOLIB select REGMAP_I2C help This is a Video4Linux2 sensor driver for the Sony @@ -115,10 +99,6 @@ config VIDEO_IMX214 config VIDEO_IMX219 tristate "Sony IMX219 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX219 camera. @@ -128,9 +108,6 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. @@ -140,9 +117,6 @@ config VIDEO_IMX258 config VIDEO_IMX274 tristate "Sony IMX274 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C help This is a V4L2 sensor driver for the Sony IMX274 @@ -150,11 +124,8 @@ config VIDEO_IMX274 config VIDEO_IMX290 tristate "Sony IMX290 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX290 camera sensor. @@ -164,10 +135,6 @@ config VIDEO_IMX290 config VIDEO_IMX296 tristate "Sony IMX296 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX296 camera. @@ -177,9 +144,6 @@ config VIDEO_IMX296 config VIDEO_IMX319 tristate "Sony IMX319 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX319 camera. @@ -190,10 +154,6 @@ config VIDEO_IMX319 config VIDEO_IMX334 tristate "Sony IMX334 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX334 camera. @@ -204,10 +164,6 @@ config VIDEO_IMX334 config VIDEO_IMX335 tristate "Sony IMX335 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX335 camera. @@ -217,9 +173,6 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. @@ -230,10 +183,6 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. @@ -244,10 +193,6 @@ config VIDEO_IMX412 config VIDEO_IMX415 tristate "Sony IMX415 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony IMX415 camera. @@ -260,35 +205,25 @@ config VIDEO_MAX9271_LIB config VIDEO_MT9M001 tristate "mt9m001 support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This driver supports MT9M001 cameras from Micron, monochrome and colour models. config VIDEO_MT9M111 tristate "mt9m111, mt9m112 and mt9m131 support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This driver supports MT9M111, MT9M112 and MT9M131 cameras from Micron/Aptina config VIDEO_MT9P031 tristate "Aptina MT9P031 support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select VIDEO_APTINA_PLL - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Aptina (Micron) mt9p031 5 Mpixel camera. config VIDEO_MT9T112 tristate "Aptina MT9T111/MT9T112 support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. @@ -298,7 +233,6 @@ config VIDEO_MT9T112 config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the @@ -306,18 +240,13 @@ config VIDEO_MT9V011 config VIDEO_MT9V032 tristate "Micron MT9V032 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_I2C - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Micron MT9V032 752x480 CMOS sensor. config VIDEO_MT9V111 tristate "Aptina MT9V111 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the Aptina/Micron MT9V111 sensor. @@ -327,10 +256,6 @@ config VIDEO_MT9V111 config VIDEO_OG01A1B tristate "OmniVision OG01A1B sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OG01A1B camera. @@ -340,10 +265,6 @@ config VIDEO_OG01A1B config VIDEO_OV01A10 tristate "OmniVision OV01A10 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV01A10 camera. @@ -353,10 +274,6 @@ config VIDEO_OV01A10 config VIDEO_OV02A10 tristate "OmniVision OV02A10 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV02A10 camera. @@ -366,10 +283,6 @@ config VIDEO_OV02A10 config VIDEO_OV08D10 tristate "OmniVision OV08D10 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV08D10 camera sensor. @@ -379,10 +292,6 @@ config VIDEO_OV08D10 config VIDEO_OV08X40 tristate "OmniVision OV08X40 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV08X40 camera. @@ -392,28 +301,18 @@ config VIDEO_OV08X40 config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV13858 camera. config VIDEO_OV13B10 tristate "OmniVision OV13B10 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV13B10 camera. config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" - depends on VIDEO_DEV && I2C - select V4L2_ASYNC help This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. @@ -423,8 +322,7 @@ config VIDEO_OV2640 config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" - depends on VIDEO_DEV && I2C && GPIOLIB - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor driver for the OmniVision OV2659 camera. @@ -434,9 +332,7 @@ config VIDEO_OV2659 config VIDEO_OV2680 tristate "OmniVision OV2680 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV2680 camera. @@ -446,10 +342,6 @@ config VIDEO_OV2680 config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV2685 camera. @@ -459,11 +351,7 @@ config VIDEO_OV2685 config VIDEO_OV2740 tristate "OmniVision OV2740 sensor support" - depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE select REGMAP_I2C help This is a Video4Linux2 sensor driver for the OmniVision @@ -474,10 +362,7 @@ config VIDEO_OV2740 config VIDEO_OV4689 tristate "OmniVision OV4689 sensor support" - depends on GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor-level driver for the OmniVision OV4689 camera. @@ -488,10 +373,7 @@ config VIDEO_OV4689 config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF - depends on GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on GPIOLIB help This is a Video4Linux2 sensor driver for the Omnivision OV5640 camera sensor with a MIPI CSI-2 interface. @@ -499,10 +381,6 @@ config VIDEO_OV5640 config VIDEO_OV5645 tristate "OmniVision OV5645 sensor support" depends on OF - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5645 camera. @@ -512,10 +390,6 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5647 camera. @@ -525,10 +399,7 @@ config VIDEO_OV5647 config VIDEO_OV5648 tristate "OmniVision OV5648 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for the OmniVision OV5648 camera. @@ -538,10 +409,6 @@ config VIDEO_OV5648 config VIDEO_OV5670 tristate "OmniVision OV5670 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5670 camera. @@ -551,10 +418,6 @@ config VIDEO_OV5670 config VIDEO_OV5675 tristate "OmniVision OV5675 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5675 camera. @@ -564,8 +427,7 @@ config VIDEO_OV5675 config VIDEO_OV5693 tristate "OmniVision OV5693 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV5693 camera. @@ -575,8 +437,6 @@ config VIDEO_OV5693 config VIDEO_OV5695 tristate "OmniVision OV5695 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. @@ -586,7 +446,6 @@ config VIDEO_OV5695 config VIDEO_OV6650 tristate "OmniVision OV6650 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. @@ -596,10 +455,6 @@ config VIDEO_OV6650 config VIDEO_OV7251 tristate "OmniVision OV7251 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV7251 camera. @@ -609,7 +464,6 @@ config VIDEO_OV7251 config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. @@ -619,8 +473,6 @@ config VIDEO_OV7640 config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" - depends on I2C && VIDEO_DEV - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 @@ -628,9 +480,7 @@ config VIDEO_OV7670 config VIDEO_OV772X tristate "OmniVision OV772x sensor support" - depends on I2C && VIDEO_DEV select REGMAP_SCCB - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. @@ -640,7 +490,6 @@ config VIDEO_OV772X config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" - depends on I2C && VIDEO_DEV select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision @@ -648,10 +497,6 @@ config VIDEO_OV7740 config VIDEO_OV8856 tristate "OmniVision OV8856 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV8856 camera sensor. @@ -661,10 +506,7 @@ config VIDEO_OV8856 config VIDEO_OV8858 tristate "OmniVision OV8858 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for OmniVision OV8858 camera sensor. @@ -674,10 +516,7 @@ config VIDEO_OV8858 config VIDEO_OV8865 tristate "OmniVision OV8865 sensor support" - depends on I2C && PM && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on PM help This is a Video4Linux2 sensor driver for OmniVision OV8865 camera sensor. @@ -688,10 +527,6 @@ config VIDEO_OV8865 config VIDEO_OV9282 tristate "OmniVision OV9282 sensor support" depends on OF_GPIO - depends on I2C && VIDEO_DEV - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV9282 camera sensor. @@ -701,16 +536,12 @@ config VIDEO_OV9282 config VIDEO_OV9640 tristate "OmniVision OV9640 sensor support" - depends on I2C && VIDEO_DEV help This is a Video4Linux2 sensor driver for the OmniVision OV9640 camera sensor. config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API select REGMAP_SCCB help This is a V4L2 sensor driver for the Omnivision @@ -718,11 +549,7 @@ config VIDEO_OV9650 config VIDEO_OV9734 tristate "OmniVision OV9734 sensor support" - depends on VIDEO_DEV && I2C depends on ACPI || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision OV9734 camera. @@ -732,10 +559,6 @@ config VIDEO_OV9734 config VIDEO_RDACM20 tristate "IMI RDACM20 camera support" - depends on I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM20 GMSL camera, used in @@ -746,10 +569,6 @@ config VIDEO_RDACM20 config VIDEO_RDACM21 tristate "IMI RDACM21 camera support" - depends on I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - select MEDIA_CONTROLLER select VIDEO_MAX9271_LIB help This driver supports the IMI RDACM21 GMSL camera, used in @@ -760,7 +579,6 @@ config VIDEO_RDACM21 config VIDEO_RJ54N1 tristate "Sharp RJ54N1CB0C sensor support" - depends on I2C && VIDEO_DEV help This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image sensor. @@ -770,39 +588,26 @@ config VIDEO_RJ54N1 config VIDEO_S5C73M3 tristate "Samsung S5C73M3 sensor support" - depends on I2C && SPI && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on SPI help This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K6A3 tristate "Samsung S5K6A3 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API help This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. config VIDEO_ST_VGXY61 tristate "ST VGXY61 sensor support" - depends on OF && GPIOLIB && VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE + depends on OF && GPIOLIB help This is a Video4Linux2 sensor driver for the ST VGXY61 camera sensor. @@ -810,7 +615,7 @@ config VIDEO_ST_VGXY61 source "drivers/media/i2c/ccs/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" -endmenu +endif menu "Lens drivers" visible if MEDIA_CAMERA_SUPPORT @@ -848,6 +653,18 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9719 + tristate "DW9719 lens voice coil support" + depends on I2C && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_ASYNC + select V4L2_CCI_I2C + help + This is a driver for the DW9719 camera lens voice coil. + This is designed for linear control of voice coil motors, + controlled via I2C serial interface. + config VIDEO_DW9768 tristate "DW9768 lens voice coil support" depends on I2C && VIDEO_DEV @@ -1625,4 +1442,51 @@ config VIDEO_THS7303 endmenu +# +# Video serializers and deserializers (e.g. FPD-Link) +# + +menu "Video serializers and deserializers" + +config VIDEO_DS90UB913 + tristate "TI DS90UB913 FPD-Link III Serializer" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB913 + FPD-Link III Serializer. + +config VIDEO_DS90UB953 + tristate "TI FPD-Link III/IV CSI-2 Serializers" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB953 + FPD-Link III Serializer and DS90UB971 FPD-Link IV Serializer. + +config VIDEO_DS90UB960 + tristate "TI FPD-Link III/IV Deserializers" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_ATR + select MEDIA_CONTROLLER + select GPIOLIB + select REGMAP_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Texas Instruments DS90UB960 + FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. + +endmenu + endif # VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c743aeb5d1ad..80b00d39b48f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -28,7 +28,11 @@ obj-$(CONFIG_VIDEO_CS3308) += cs3308.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_DS90UB913) += ds90ub913.o +obj-$(CONFIG_VIDEO_DS90UB953) += ds90ub953.o +obj-$(CONFIG_VIDEO_DS90UB960) += ds90ub960.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9719) += dw9719.o obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 5f605b9be3b1..1543d24f522c 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -349,7 +349,6 @@ static void ad5820_remove(struct i2c_client *client) static const struct i2c_device_id ad5820_id_table[] = { { "ad5820", 0 }, { "ad5821", 0 }, - { "ad5823", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); @@ -357,7 +356,6 @@ MODULE_DEVICE_TABLE(i2c, ad5820_id_table); static const struct of_device_id ad5820_of_table[] = { { .compatible = "adi,ad5820" }, { .compatible = "adi,ad5821" }, - { .compatible = "adi,ad5823" }, { } }; MODULE_DEVICE_TABLE(of, ad5820_of_table); diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index bd4f3fe0e309..a5a7cb228896 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -300,9 +300,6 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) MEDIA_ENT_F_VID_IF_BRIDGE, is_txa(tx) ? "txa" : "txb"); - /* Ensure that matching is based upon the endpoint fwnodes */ - tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]); - /* Register internal ops for incremental subdev registration */ tx->sd.internal_ops = &adv748x_csi2_internal_ops; @@ -314,10 +311,15 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) if (ret) return ret; - ret = adv748x_csi2_init_controls(tx); + ret = v4l2_async_subdev_endpoint_add(&tx->sd, + of_fwnode_handle(state->endpoints[tx->port])); if (ret) goto err_free_media; + ret = adv748x_csi2_init_controls(tx); + if (ret) + goto err_cleanup_subdev; + ret = v4l2_async_register_subdev(&tx->sd); if (ret) goto err_free_ctrl; @@ -326,6 +328,8 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) err_free_ctrl: v4l2_ctrl_handler_free(&tx->ctrl_hdl); +err_cleanup_subdev: + v4l2_subdev_cleanup(&tx->sd); err_free_media: media_entity_cleanup(&tx->sd.entity); @@ -340,4 +344,5 @@ void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) v4l2_async_unregister_subdev(&tx->sd); media_entity_cleanup(&tx->sd.entity); v4l2_ctrl_handler_free(&tx->ctrl_hdl); + v4l2_subdev_cleanup(&tx->sd); } diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c index fcc39360cc50..cf8858cb13d4 100644 --- a/drivers/media/i2c/ccs-pll.c +++ b/drivers/media/i2c/ccs-pll.c @@ -296,7 +296,7 @@ __ccs_pll_calculate_vt_tree(struct device *dev, struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk; u32 more_mul; - u16 best_pix_div = SHRT_MAX >> 1, best_div; + u16 best_pix_div = SHRT_MAX >> 1, best_div = lim_bk->max_sys_clk_div; u16 vt_div, min_sys_div, max_sys_div, sys_div; pll_fr->pll_ip_clk_freq_hz = diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig index 71671db3d993..b55c93a2e204 100644 --- a/drivers/media/i2c/ccs/Kconfig +++ b/drivers/media/i2c/ccs/Kconfig @@ -1,11 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_CCS tristate "MIPI CCS/SMIA++/SMIA sensor support" - depends on I2C && VIDEO_DEV && HAVE_CLK - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API + depends on HAVE_CLK select VIDEO_CCS_PLL - select V4L2_FWNODE help This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant camera sensors. diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c index 45f2b2f55ec5..08400edf77ce 100644 --- a/drivers/media/i2c/ccs/ccs-data.c +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -464,8 +464,7 @@ static int ccs_data_parse_rules(struct bin_container *bin, rule_payload = __rule_type + 1; rule_plen2 = rule_plen - sizeof(*__rule_type); - switch (*__rule_type) { - case CCS_DATA_BLOCK_RULE_ID_IF: { + if (*__rule_type == CCS_DATA_BLOCK_RULE_ID_IF) { const struct __ccs_data_block_rule_if *__if_rules = rule_payload; const size_t __num_if_rules = @@ -514,49 +513,61 @@ static int ccs_data_parse_rules(struct bin_container *bin, rules->if_rules = if_rule; rules->num_if_rules = __num_if_rules; } - break; - } - case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: - rval = ccs_data_parse_reg_rules(bin, &rules->read_only_regs, - &rules->num_read_only_regs, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_FFD: - rval = ccs_data_parse_ffd(bin, &rules->frame_format, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_MSR: - rval = ccs_data_parse_reg_rules(bin, - &rules->manufacturer_regs, - &rules->num_manufacturer_regs, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: - rval = ccs_data_parse_pdaf_readout(bin, - &rules->pdaf_readout, - rule_payload, - rule_payload + rule_plen2, - dev); - if (rval) - return rval; - break; - default: - dev_dbg(dev, - "Don't know how to handle rule type %u!\n", - *__rule_type); - return -EINVAL; + } else { + /* Check there was an if rule before any other rules */ + if (bin->base && !rules) + return -EINVAL; + + switch (*__rule_type) { + case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: + rval = ccs_data_parse_reg_rules(bin, + rules ? + &rules->read_only_regs : NULL, + rules ? + &rules->num_read_only_regs : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_FFD: + rval = ccs_data_parse_ffd(bin, rules ? + &rules->frame_format : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_MSR: + rval = ccs_data_parse_reg_rules(bin, + rules ? + &rules->manufacturer_regs : NULL, + rules ? + &rules->num_manufacturer_regs : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: + rval = ccs_data_parse_pdaf_readout(bin, + rules ? + &rules->pdaf_readout : NULL, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + default: + dev_dbg(dev, + "Don't know how to handle rule type %u!\n", + *__rule_type); + return -EINVAL; + } } __next_rule = __next_rule + rule_hlen + rule_plen; } diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c new file mode 100644 index 000000000000..4bfa3b3cf619 --- /dev/null +++ b/drivers/media/i2c/ds90ub913.c @@ -0,0 +1,903 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB913 video serializer + * + * Based on a driver from Luca Ceresoli <luca@lucaceresoli.net> + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/fwnode.h> +#include <linux/gpio/driver.h> +#include <linux/i2c-atr.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include <media/i2c/ds90ub9xx.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define UB913_PAD_SINK 0 +#define UB913_PAD_SOURCE 1 + +/* + * UB913 has 4 gpios, but gpios 3 and 4 are reserved for external oscillator + * mode. Thus we only support 2 gpios for now. + */ +#define UB913_NUM_GPIOS 2 + +#define UB913_REG_RESET_CTL 0x01 +#define UB913_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB913_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB913_REG_GENERAL_CFG 0x03 +#define UB913_REG_GENERAL_CFG_CRC_ERR_RESET BIT(5) +#define UB913_REG_GENERAL_CFG_PCLK_RISING BIT(0) + +#define UB913_REG_MODE_SEL 0x05 +#define UB913_REG_MODE_SEL_MODE_OVERRIDE BIT(5) +#define UB913_REG_MODE_SEL_MODE_UP_TO_DATE BIT(4) +#define UB913_REG_MODE_SEL_MODE_MASK GENMASK(3, 0) + +#define UB913_REG_CRC_ERRORS_LSB 0x0a +#define UB913_REG_CRC_ERRORS_MSB 0x0b + +#define UB913_REG_GENERAL_STATUS 0x0c + +#define UB913_REG_GPIO_CFG(n) (0x0d + (n)) +#define UB913_REG_GPIO_CFG_ENABLE(n) BIT(0 + (n) * 4) +#define UB913_REG_GPIO_CFG_DIR_INPUT(n) BIT(1 + (n) * 4) +#define UB913_REG_GPIO_CFG_REMOTE_EN(n) BIT(2 + (n) * 4) +#define UB913_REG_GPIO_CFG_OUT_VAL(n) BIT(3 + (n) * 4) +#define UB913_REG_GPIO_CFG_MASK(n) (0xf << ((n) * 4)) + +#define UB913_REG_SCL_HIGH_TIME 0x11 +#define UB913_REG_SCL_LOW_TIME 0x12 + +#define UB913_REG_PLL_OVR 0x35 + +struct ub913_data { + struct i2c_client *client; + struct regmap *regmap; + struct clk *clkin; + + struct gpio_chip gpio_chip; + + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + + u64 enabled_source_streams; + + struct clk_hw *clkout_clk_hw; + + struct ds90ub9xx_platform_data *plat_data; + + bool pclk_polarity_rising; +}; + +static inline struct ub913_data *sd_to_ub913(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub913_data, sd); +} + +struct ub913_format_info { + u32 incode; + u32 outcode; +}; + +static const struct ub913_format_info ub913_formats[] = { + /* Only RAW10 with 8-bit payload is supported at the moment */ + { .incode = MEDIA_BUS_FMT_YUYV8_2X8, .outcode = MEDIA_BUS_FMT_YUYV8_1X16 }, + { .incode = MEDIA_BUS_FMT_UYVY8_2X8, .outcode = MEDIA_BUS_FMT_UYVY8_1X16 }, + { .incode = MEDIA_BUS_FMT_VYUY8_2X8, .outcode = MEDIA_BUS_FMT_VYUY8_1X16 }, + { .incode = MEDIA_BUS_FMT_YVYU8_2X8, .outcode = MEDIA_BUS_FMT_YVYU8_1X16 }, +}; + +static const struct ub913_format_info *ub913_find_format(u32 incode) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ub913_formats); i++) { + if (ub913_formats[i].incode == incode) + return &ub913_formats[i]; + } + + return NULL; +} + +static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret < 0) { + dev_err(&priv->client->dev, + "Cannot read register 0x%02x: %d!\n", reg, ret); + return ret; + } + + *val = v; + return 0; +} + +static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) +{ + int ret; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "Cannot write register 0x%02x: %d!\n", reg, ret); + + return ret; +} + +/* + * GPIO chip + */ +static int ub913_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ub913_data *priv = gpiochip_get_data(gc); + unsigned int reg_idx = offset / 2; + unsigned int field_idx = offset % 2; + + return regmap_update_bits(priv->regmap, UB913_REG_GPIO_CFG(reg_idx), + UB913_REG_GPIO_CFG_MASK(field_idx), + UB913_REG_GPIO_CFG_ENABLE(field_idx) | + (value ? UB913_REG_GPIO_CFG_OUT_VAL(field_idx) : + 0)); +} + +static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + ub913_gpio_direction_out(gc, offset, value); +} + +static int ub913_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0]; +} + +static int ub913_gpiochip_probe(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int ret; + + /* Initialize GPIOs 0 and 1 to local control, tri-state */ + ub913_write(priv, UB913_REG_GPIO_CFG(0), 0); + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->can_sleep = true; + gc->ngpio = UB913_NUM_GPIOS; + gc->get_direction = ub913_gpio_get_direction; + gc->direction_output = ub913_gpio_direction_out; + gc->set = ub913_gpio_set; + gc->of_xlate = ub913_gpio_of_xlate; + gc->of_gpio_n_cells = 2; + + ret = gpiochip_add_data(gc, priv); + if (ret) { + dev_err(dev, "Failed to add GPIOs: %d\n", ret); + return ret; + } + + return 0; +} + +static void ub913_gpiochip_remove(struct ub913_data *priv) +{ + gpiochip_remove(&priv->gpio_chip); +} + +static const struct regmap_config ub913_regmap_config = { + .name = "ds90ub913", + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_DEFAULT, + .val_format_endian = REGMAP_ENDIAN_DEFAULT, +}; + +/* + * V4L2 + */ + +static int ub913_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub913_data *priv = sd_to_ub913(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, + UB913_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams |= streams_mask; + + return 0; +} + +static int ub913_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub913_data *priv = sd_to_ub913(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, + UB913_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams &= ~streams_mask; + + return 0; +} + +static int _ub913_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt in_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + static const struct v4l2_mbus_framefmt out_format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; i++) { + if (stream_configs->configs[i].pad == UB913_PAD_SINK) + stream_configs->configs[i].fmt = in_format; + else + stream_configs->configs[i].fmt = out_format; + } + + return 0; +} + +static int ub913_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub913_data *priv = sd_to_ub913(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _ub913_set_routing(sd, state, routing); +} + +static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub913_data *priv = sd_to_ub913(sd); + const struct v4l2_subdev_krouting *routing; + struct v4l2_mbus_frame_desc source_fd; + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret; + + if (pad != UB913_PAD_SOURCE) + return -EINVAL; + + ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, + priv->source_sd_pad, &source_fd); + if (ret) + return ret; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + routing = &state->routing; + + for_each_active_route(routing, route) { + unsigned int i; + + if (route->source_pad != pad) + continue; + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) + break; + } + + if (i == source_fd.num_entries) { + dev_err(&priv->client->dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_fd.entry[i].flags; + fd->entry[fd->num_entries].length = source_fd.entry[i].length; + fd->entry[fd->num_entries].pixelcode = + source_fd.entry[i].pixelcode; + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub913_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub913_data *priv = sd_to_ub913(sd); + struct v4l2_mbus_framefmt *fmt; + const struct ub913_format_info *finfo; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* Source format is fully defined by the sink format, so not settable */ + if (format->pad == UB913_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + finfo = ub913_find_format(format->format.code); + if (!finfo) { + finfo = &ub913_formats[0]; + format->format.code = finfo->incode; + } + + /* Set sink format */ + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format, and adjust the mbus code */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + format->format.code = finfo->outcode; + + *fmt = format->format; + + return 0; +} + +static int ub913_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UB913_PAD_SINK, + .sink_stream = 0, + .source_pad = UB913_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub913_set_routing(sd, state, &routing); +} + +static int ub913_log_status(struct v4l2_subdev *sd) +{ + struct ub913_data *priv = sd_to_ub913(sd); + struct device *dev = &priv->client->dev; + u8 v = 0, v1 = 0, v2 = 0; + + ub913_read(priv, UB913_REG_MODE_SEL, &v); + dev_info(dev, "MODE_SEL %#02x\n", v); + + ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1); + ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2); + dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); + + /* clear CRC errors */ + ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + ub913_write(priv, UB913_REG_GENERAL_CFG, + v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET); + ub913_write(priv, UB913_REG_GENERAL_CFG, v); + + ub913_read(priv, UB913_REG_GENERAL_STATUS, &v); + dev_info(dev, "GENERAL_STATUS %#02x\n", v); + + ub913_read(priv, UB913_REG_PLL_OVR, &v); + dev_info(dev, "PLL_OVR %#02x\n", v); + + return 0; +} + +static const struct v4l2_subdev_core_ops ub913_subdev_core_ops = { + .log_status = ub913_log_status, +}; + +static const struct v4l2_subdev_pad_ops ub913_pad_ops = { + .enable_streams = ub913_enable_streams, + .disable_streams = ub913_disable_streams, + .set_routing = ub913_set_routing, + .get_frame_desc = ub913_get_frame_desc, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub913_set_fmt, + .init_cfg = ub913_init_cfg, +}; + +static const struct v4l2_subdev_ops ub913_subdev_ops = { + .core = &ub913_subdev_core_ops, + .pad = &ub913_pad_ops, +}; + +static const struct media_entity_operations ub913_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int ub913_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct ub913_data *priv = sd_to_ub913(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, UB913_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations ub913_notify_ops = { + .bound = ub913_notify_bound, +}; + +static int ub913_v4l2_notifier_register(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB913_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &ub913_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub913_v4l2_nf_unregister(struct ub913_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +static int ub913_register_clkout(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + const char *name; + int ret; + + name = kasprintf(GFP_KERNEL, "ds90ub913.%s.clk_out", dev_name(dev)); + if (!name) + return -ENOMEM; + + priv->clkout_clk_hw = devm_clk_hw_register_fixed_factor(dev, name, + __clk_get_name(priv->clkin), 0, 1, 2); + + kfree(name); + + if (IS_ERR(priv->clkout_clk_hw)) + return dev_err_probe(dev, PTR_ERR(priv->clkout_clk_hw), + "Cannot register clkout hw\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + priv->clkout_clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; +} + +static int ub913_i2c_master_init(struct ub913_data *priv) +{ + /* i2c fast mode */ + u32 scl_high = 600 + 300; /* high period + rise time, ns */ + u32 scl_low = 1300 + 300; /* low period + fall time, ns */ + unsigned long ref; + int ret; + + ref = clk_get_rate(priv->clkin) / 2; + + scl_high = div64_u64((u64)scl_high * ref, 1000000000); + scl_low = div64_u64((u64)scl_low * ref, 1000000000); + + ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high); + if (ret) + return ret; + + ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low); + if (ret) + return ret; + + return 0; +} + +static int ub913_add_i2c_adapter(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *i2c_handle; + int ret; + + i2c_handle = device_get_named_child_node(dev, "i2c"); + if (!i2c_handle) + return 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, + dev, i2c_handle); + + fwnode_handle_put(i2c_handle); + + if (ret) + return ret; + + return 0; +} + +static int ub913_parse_dt(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_PARALLEL, + }; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB913_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "No sink endpoint\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret) + return dev_err_probe(dev, ret, + "failed to parse sink endpoint data\n"); + + if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + priv->pclk_polarity_rising = true; + else if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + priv->pclk_polarity_rising = false; + else + return dev_err_probe(dev, -EINVAL, + "bad value for 'pclk-sample'\n"); + + return 0; +} + +static int ub913_hw_init(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + bool mode_override; + u8 mode; + int ret; + u8 v; + + ret = ub913_read(priv, UB913_REG_MODE_SEL, &v); + if (ret) + return ret; + + if (!(v & UB913_REG_MODE_SEL_MODE_UP_TO_DATE)) + return dev_err_probe(dev, -ENODEV, + "Mode value not stabilized\n"); + + mode_override = v & UB913_REG_MODE_SEL_MODE_OVERRIDE; + mode = v & UB913_REG_MODE_SEL_MODE_MASK; + + dev_dbg(dev, "mode from %s: %#x\n", + mode_override ? "reg" : "deserializer", mode); + + ret = ub913_i2c_master_init(priv); + if (ret) + return dev_err_probe(dev, ret, "i2c master init failed\n"); + + ub913_read(priv, UB913_REG_GENERAL_CFG, &v); + v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING; + v |= priv->pclk_polarity_rising ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0; + ub913_write(priv, UB913_REG_GENERAL_CFG, v); + + return 0; +} + +static int ub913_subdev_init(struct ub913_data *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub913_entity_ops; + + priv->pads[0].flags = MEDIA_PAD_FL_SINK; + priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub913_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub913_v4l2_nf_unregister(priv); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void ub913_subdev_uninit(struct ub913_data *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + ub913_v4l2_nf_unregister(priv); + v4l2_subdev_cleanup(&priv->sd); + fwnode_handle_put(priv->sd.fwnode); + media_entity_cleanup(&priv->sd.entity); +} + +static int ub913_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub913_data *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->plat_data = dev_get_platdata(&client->dev); + if (!priv->plat_data) + return dev_err_probe(dev, -ENODEV, "Platform data missing\n"); + + priv->regmap = devm_regmap_init_i2c(client, &ub913_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Failed to init regmap\n"); + + /* + * ub913 can also work without ext clock, but that is not supported by + * the driver yet. + */ + priv->clkin = devm_clk_get(dev, "clkin"); + if (IS_ERR(priv->clkin)) + return dev_err_probe(dev, PTR_ERR(priv->clkin), + "Cannot get CLKIN\n"); + + ret = ub913_parse_dt(priv); + if (ret) + return ret; + + ret = ub913_hw_init(priv); + if (ret) + return ret; + + ret = ub913_gpiochip_probe(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to init gpiochip\n"); + + ret = ub913_register_clkout(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clkout\n"); + goto err_gpiochip_remove; + } + + ret = ub913_subdev_init(priv); + if (ret) + goto err_gpiochip_remove; + + ret = ub913_add_i2c_adapter(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + goto err_subdev_uninit; + } + + return 0; + +err_subdev_uninit: + ub913_subdev_uninit(priv); +err_gpiochip_remove: + ub913_gpiochip_remove(priv); + + return ret; +} + +static void ub913_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub913_data *priv = sd_to_ub913(sd); + + i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port); + + ub913_subdev_uninit(priv); + + ub913_gpiochip_remove(priv); +} + +static const struct i2c_device_id ub913_id[] = { { "ds90ub913a-q1", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, ub913_id); + +static const struct of_device_id ub913_dt_ids[] = { + { .compatible = "ti,ds90ub913a-q1" }, + {} +}; +MODULE_DEVICE_TABLE(of, ub913_dt_ids); + +static struct i2c_driver ds90ub913_driver = { + .probe = ub913_probe, + .remove = ub913_remove, + .id_table = ub913_id, + .driver = { + .name = "ds90ub913a", + .of_match_table = ub913_dt_ids, + }, +}; +module_i2c_driver(ds90ub913_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments DS90UB913 FPD-Link III Serializer Driver"); +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c new file mode 100644 index 000000000000..dc394e22a42c --- /dev/null +++ b/drivers/media/i2c/ds90ub953.c @@ -0,0 +1,1430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB953 video serializer + * + * Based on a driver from Luca Ceresoli <luca@lucaceresoli.net> + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/fwnode.h> +#include <linux/gpio/driver.h> +#include <linux/i2c-atr.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/rational.h> +#include <linux/regmap.h> + +#include <media/i2c/ds90ub9xx.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define UB953_PAD_SINK 0 +#define UB953_PAD_SOURCE 1 + +#define UB953_NUM_GPIOS 4 + +#define UB953_DEFAULT_CLKOUT_RATE 25000000UL + +#define UB953_REG_RESET_CTL 0x01 +#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB953_REG_GENERAL_CFG 0x02 +#define UB953_REG_GENERAL_CFG_CONT_CLK BIT(6) +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT 4 +#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK GENMASK(5, 4) +#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE BIT(1) +#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE BIT(0) + +#define UB953_REG_MODE_SEL 0x03 +#define UB953_REG_MODE_SEL_MODE_DONE BIT(3) +#define UB953_REG_MODE_SEL_MODE_OVERRIDE BIT(4) +#define UB953_REG_MODE_SEL_MODE_MASK GENMASK(2, 0) + +#define UB953_REG_CLKOUT_CTRL0 0x06 +#define UB953_REG_CLKOUT_CTRL1 0x07 + +#define UB953_REG_SCL_HIGH_TIME 0x0b +#define UB953_REG_SCL_LOW_TIME 0x0c + +#define UB953_REG_LOCAL_GPIO_DATA 0x0d +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) + +#define UB953_REG_GPIO_INPUT_CTRL 0x0e +#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) +#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) + +#define UB953_REG_REV_MASK_ID 0x50 +#define UB953_REG_GENERAL_STATUS 0x52 + +#define UB953_REG_GPIO_PIN_STS 0x53 +#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) + +#define UB953_REG_BIST_ERR_CNT 0x54 +#define UB953_REG_CRC_ERR_CNT1 0x55 +#define UB953_REG_CRC_ERR_CNT2 0x56 + +#define UB953_REG_CSI_ERR_CNT 0x5c +#define UB953_REG_CSI_ERR_STATUS 0x5d +#define UB953_REG_CSI_ERR_DLANE01 0x5e +#define UB953_REG_CSI_ERR_DLANE23 0x5f +#define UB953_REG_CSI_ERR_CLK_LANE 0x60 +#define UB953_REG_CSI_PKT_HDR_VC_ID 0x61 +#define UB953_REG_PKT_HDR_WC_LSB 0x62 +#define UB953_REG_PKT_HDR_WC_MSB 0x63 +#define UB953_REG_CSI_ECC 0x64 + +#define UB953_REG_IND_ACC_CTL 0xb0 +#define UB953_REG_IND_ACC_ADDR 0xb1 +#define UB953_REG_IND_ACC_DATA 0xb2 + +#define UB953_REG_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB953_REG_FPD3_RX_ID_LEN 6 + +/* Indirect register blocks */ +#define UB953_IND_TARGET_PAT_GEN 0x00 +#define UB953_IND_TARGET_FPD3_TX 0x01 +#define UB953_IND_TARGET_DIE_ID 0x02 + +#define UB953_IND_PGEN_CTL 0x01 +#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) +#define UB953_IND_PGEN_CFG 0x02 +#define UB953_IND_PGEN_CSI_DI 0x03 +#define UB953_IND_PGEN_LINE_SIZE1 0x04 +#define UB953_IND_PGEN_LINE_SIZE0 0x05 +#define UB953_IND_PGEN_BAR_SIZE1 0x06 +#define UB953_IND_PGEN_BAR_SIZE0 0x07 +#define UB953_IND_PGEN_ACT_LPF1 0x08 +#define UB953_IND_PGEN_ACT_LPF0 0x09 +#define UB953_IND_PGEN_TOT_LPF1 0x0a +#define UB953_IND_PGEN_TOT_LPF0 0x0b +#define UB953_IND_PGEN_LINE_PD1 0x0c +#define UB953_IND_PGEN_LINE_PD0 0x0d +#define UB953_IND_PGEN_VBP 0x0e +#define UB953_IND_PGEN_VFP 0x0f +#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ + +/* Note: Only sync mode supported for now */ +enum ub953_mode { + /* FPD-Link III CSI-2 synchronous mode */ + UB953_MODE_SYNC, + /* FPD-Link III CSI-2 non-synchronous mode, external ref clock */ + UB953_MODE_NONSYNC_EXT, + /* FPD-Link III CSI-2 non-synchronous mode, internal ref clock */ + UB953_MODE_NONSYNC_INT, + /* FPD-Link III DVP mode */ + UB953_MODE_DVP, +}; + +struct ub953_hw_data { + const char *model; + bool is_ub971; +}; + +struct ub953_clkout_data { + u32 hs_div; + u32 m; + u32 n; + unsigned long rate; +}; + +struct ub953_data { + const struct ub953_hw_data *hw_data; + + struct i2c_client *client; + struct regmap *regmap; + struct clk *clkin; + + u32 num_data_lanes; + bool non_continous_clk; + + struct gpio_chip gpio_chip; + + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + + u64 enabled_source_streams; + + /* lock for register access */ + struct mutex reg_lock; + + u8 current_indirect_target; + + struct clk_hw clkout_clk_hw; + + enum ub953_mode mode; + + const struct ds90ub9xx_platform_data *plat_data; +}; + +static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub953_data, sd); +} + +/* + * HW Access + */ + +static int ub953_read(struct ub953_data *priv, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(&priv->client->dev, "Cannot read register 0x%02x: %d\n", + reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub953_write(struct ub953_data *priv, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(&priv->client->dev, + "Cannot write register 0x%02x: %d\n", reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub953_select_ind_reg_block(struct ub953_data *priv, u8 block) +{ + struct device *dev = &priv->client->dev; + int ret; + + if (priv->current_indirect_target == block) + return 0; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_CTL, block << 2); + if (ret) { + dev_err(dev, "%s: cannot select indirect target %u (%d)\n", + __func__, block, ret); + return ret; + } + + priv->current_indirect_target = block; + + return 0; +} + +__maybe_unused +static int ub953_read_ind(struct ub953_data *priv, u8 block, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub953_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_read(priv->regmap, UB953_REG_IND_ACC_DATA, &v); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +__maybe_unused +static int ub953_write_ind(struct ub953_data *priv, u8 block, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub953_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_ADDR, reg); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_write(priv->regmap, UB953_REG_IND_ACC_DATA, val); + if (ret) { + dev_err(&priv->client->dev, + "Write to IND_ACC_DATA failed when writing %u:%x02x\n: %d\n", + block, reg, ret); + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +/* + * GPIO chip + */ +static int ub953_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v); + if (ret) + return ret; + + if (v & UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)) + return GPIO_LINE_DIRECTION_IN; + else + return GPIO_LINE_DIRECTION_OUT; +} + +static int ub953_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + return regmap_update_bits(priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)); +} + +static int ub953_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + + ret = regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : + 0); + + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset)); +} + +static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v); + if (ret) + return ret; + + return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset)); +} + +static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : + 0); +} + +static int ub953_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0]; +} + +static int ub953_gpiochip_probe(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int ret; + + /* Set all GPIOs to local input mode */ + ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); + ub953_write(priv, UB953_REG_GPIO_INPUT_CTRL, 0xf); + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->can_sleep = true; + gc->ngpio = UB953_NUM_GPIOS; + gc->get_direction = ub953_gpio_get_direction; + gc->direction_input = ub953_gpio_direction_in; + gc->direction_output = ub953_gpio_direction_out; + gc->get = ub953_gpio_get; + gc->set = ub953_gpio_set; + gc->of_xlate = ub953_gpio_of_xlate; + gc->of_gpio_n_cells = 2; + + ret = gpiochip_add_data(gc, priv); + if (ret) { + dev_err(dev, "Failed to add GPIOs: %d\n", ret); + return ret; + } + + return 0; +} + +static void ub953_gpiochip_remove(struct ub953_data *priv) +{ + gpiochip_remove(&priv->gpio_chip); +} + +/* + * V4L2 + */ + +static int _ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub953_data *priv = sd_to_ub953(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _ub953_set_routing(sd, state, routing); +} + +static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct v4l2_mbus_frame_desc source_fd; + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret; + + if (pad != UB953_PAD_SOURCE) + return -EINVAL; + + ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, + priv->source_sd_pad, &source_fd); + if (ret) + return ret; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + unsigned int i; + + if (route->source_pad != pad) + continue; + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) { + source_entry = &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_err(&priv->client->dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_entry->flags; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; + fd->entry[fd->num_entries].bus.csi2.vc = + source_entry->bus.csi2.vc; + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub953_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == UB953_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + /* Set sink format */ + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int ub953_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = UB953_PAD_SINK, + .sink_stream = 0, + .source_pad = UB953_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub953_set_routing(sd, state, &routing); +} + +static int ub953_log_status(struct v4l2_subdev *sd) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct device *dev = &priv->client->dev; + u8 v = 0, v1 = 0, v2 = 0; + unsigned int i; + char id[UB953_REG_FPD3_RX_ID_LEN]; + u8 gpio_local_data = 0; + u8 gpio_input_ctrl = 0; + u8 gpio_pin_sts = 0; + + for (i = 0; i < sizeof(id); i++) + ub953_read(priv, UB953_REG_FPD3_RX_ID(i), &id[i]); + + dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); + + ub953_read(priv, UB953_REG_GENERAL_STATUS, &v); + dev_info(dev, "GENERAL_STATUS %#02x\n", v); + + ub953_read(priv, UB953_REG_CRC_ERR_CNT1, &v1); + ub953_read(priv, UB953_REG_CRC_ERR_CNT2, &v2); + dev_info(dev, "CRC error count %u\n", v1 | (v2 << 8)); + + ub953_read(priv, UB953_REG_CSI_ERR_CNT, &v); + dev_info(dev, "CSI error count %u\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_STATUS, &v); + dev_info(dev, "CSI_ERR_STATUS %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_DLANE01, &v); + dev_info(dev, "CSI_ERR_DLANE01 %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_DLANE23, &v); + dev_info(dev, "CSI_ERR_DLANE23 %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_ERR_CLK_LANE, &v); + dev_info(dev, "CSI_ERR_CLK_LANE %#02x\n", v); + + ub953_read(priv, UB953_REG_CSI_PKT_HDR_VC_ID, &v); + dev_info(dev, "CSI packet header VC %u ID %u\n", v >> 6, v & 0x3f); + + ub953_read(priv, UB953_REG_PKT_HDR_WC_LSB, &v1); + ub953_read(priv, UB953_REG_PKT_HDR_WC_MSB, &v2); + dev_info(dev, "CSI packet header WC %u\n", (v2 << 8) | v1); + + ub953_read(priv, UB953_REG_CSI_ECC, &v); + dev_info(dev, "CSI ECC %#02x\n", v); + + ub953_read(priv, UB953_REG_LOCAL_GPIO_DATA, &gpio_local_data); + ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &gpio_input_ctrl); + ub953_read(priv, UB953_REG_GPIO_PIN_STS, &gpio_pin_sts); + + for (i = 0; i < UB953_NUM_GPIOS; i++) { + dev_info(dev, + "GPIO%u: remote: %u is_input: %u is_output: %u val: %u sts: %u\n", + i, + !!(gpio_local_data & UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(i)), + !!(gpio_input_ctrl & UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(i)), + !!(gpio_input_ctrl & UB953_REG_GPIO_INPUT_CTRL_OUT_EN(i)), + !!(gpio_local_data & UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(i)), + !!(gpio_pin_sts & UB953_REG_GPIO_PIN_STS_GPIO_STS(i))); + } + + return 0; +} + +static int ub953_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub953_data *priv = sd_to_ub953(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE, + UB953_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams |= streams_mask; + + return 0; +} + +static int ub953_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct ub953_data *priv = sd_to_ub953(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, UB953_PAD_SOURCE, + UB953_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams &= ~streams_mask; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ub953_pad_ops = { + .enable_streams = ub953_enable_streams, + .disable_streams = ub953_disable_streams, + .set_routing = ub953_set_routing, + .get_frame_desc = ub953_get_frame_desc, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub953_set_fmt, + .init_cfg = ub953_init_cfg, +}; + +static const struct v4l2_subdev_core_ops ub953_subdev_core_ops = { + .log_status = ub953_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ub953_subdev_ops = { + .core = &ub953_subdev_core_ops, + .pad = &ub953_pad_ops, +}; + +static const struct media_entity_operations ub953_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int ub953_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct ub953_data *priv = sd_to_ub953(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations ub953_notify_ops = { + .bound = ub953_notify_bound, +}; + +static int ub953_v4l2_notifier_register(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB953_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &ub953_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub953_v4l2_notifier_unregister(struct ub953_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +/* + * Probing + */ + +static int ub953_i2c_master_init(struct ub953_data *priv) +{ + /* i2c fast mode */ + u32 ref = 26250000; + u32 scl_high = 915; /* ns */ + u32 scl_low = 1641; /* ns */ + int ret; + + scl_high = div64_u64((u64)scl_high * ref, 1000000000) - 5; + scl_low = div64_u64((u64)scl_low * ref, 1000000000) - 5; + + ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low); + if (ret) + return ret; + + return 0; +} + +static u64 ub953_get_fc_rate(struct ub953_data *priv) +{ + switch (priv->mode) { + case UB953_MODE_SYNC: + if (priv->hw_data->is_ub971) + return priv->plat_data->bc_rate * 160ull; + else + return priv->plat_data->bc_rate / 2 * 160ull; + + case UB953_MODE_NONSYNC_EXT: + /* CLKIN_DIV = 1 always */ + return clk_get_rate(priv->clkin) * 80ull; + + default: + /* Not supported */ + return 0; + } +} + +static unsigned long ub953_calc_clkout_ub953(struct ub953_data *priv, + unsigned long target, u64 fc, + u8 *hs_div, u8 *m, u8 *n) +{ + /* + * We always use 4 as a pre-divider (HS_CLK_DIV = 2). + * + * According to the datasheet: + * - "HS_CLK_DIV typically should be set to either 16, 8, or 4 (default)." + * - "if it is not possible to have an integer ratio of N/M, it is best to + * select a smaller value for HS_CLK_DIV. + * + * For above reasons the default HS_CLK_DIV seems the best in the average + * case. Use always that value to keep the code simple. + */ + static const unsigned long hs_clk_div = 4; + + u64 fc_divided; + unsigned long mul, div; + unsigned long res; + + /* clkout = fc / hs_clk_div * m / n */ + + fc_divided = div_u64(fc, hs_clk_div); + + rational_best_approximation(target, fc_divided, (1 << 5) - 1, + (1 << 8) - 1, &mul, &div); + + res = div_u64(fc_divided * mul, div); + + *hs_div = hs_clk_div; + *m = mul; + *n = div; + + return res; +} + +static unsigned long ub953_calc_clkout_ub971(struct ub953_data *priv, + unsigned long target, u64 fc, + u8 *m, u8 *n) +{ + u64 fc_divided; + unsigned long mul, div; + unsigned long res; + + /* clkout = fc * m / (8 * n) */ + + fc_divided = div_u64(fc, 8); + + rational_best_approximation(target, fc_divided, (1 << 5) - 1, + (1 << 8) - 1, &mul, &div); + + res = div_u64(fc_divided * mul, div); + + *m = mul; + *n = div; + + return res; +} + +static void ub953_calc_clkout_params(struct ub953_data *priv, + unsigned long target_rate, + struct ub953_clkout_data *clkout_data) +{ + struct device *dev = &priv->client->dev; + unsigned long clkout_rate; + u64 fc_rate; + + fc_rate = ub953_get_fc_rate(priv); + + if (priv->hw_data->is_ub971) { + u8 m, n; + + clkout_rate = ub953_calc_clkout_ub971(priv, target_rate, + fc_rate, &m, &n); + + clkout_data->m = m; + clkout_data->n = n; + + dev_dbg(dev, "%s %llu * %u / (8 * %u) = %lu (requested %lu)", + __func__, fc_rate, m, n, clkout_rate, target_rate); + } else { + u8 hs_div, m, n; + + clkout_rate = ub953_calc_clkout_ub953(priv, target_rate, + fc_rate, &hs_div, &m, &n); + + clkout_data->hs_div = hs_div; + clkout_data->m = m; + clkout_data->n = n; + + dev_dbg(dev, "%s %llu / %u * %u / %u = %lu (requested %lu)", + __func__, fc_rate, hs_div, m, n, clkout_rate, + target_rate); + } + + clkout_data->rate = clkout_rate; +} + +static void ub953_write_clkout_regs(struct ub953_data *priv, + const struct ub953_clkout_data *clkout_data) +{ + u8 clkout_ctrl0, clkout_ctrl1; + + if (priv->hw_data->is_ub971) + clkout_ctrl0 = clkout_data->m; + else + clkout_ctrl0 = (__ffs(clkout_data->hs_div) << 5) | + clkout_data->m; + + clkout_ctrl1 = clkout_data->n; + + ub953_write(priv, UB953_REG_CLKOUT_CTRL0, clkout_ctrl0); + ub953_write(priv, UB953_REG_CLKOUT_CTRL1, clkout_ctrl1); +} + +static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct device *dev = &priv->client->dev; + u8 ctrl0, ctrl1; + u32 mul, div; + u64 fc_rate; + u32 hs_clk_div; + u64 rate; + int ret; + + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL0, &ctrl0); + if (ret) { + dev_err(dev, "Failed to read CLKOUT_CTRL0: %d\n", ret); + return 0; + } + + ret = ub953_read(priv, UB953_REG_CLKOUT_CTRL1, &ctrl1); + if (ret) { + dev_err(dev, "Failed to read CLKOUT_CTRL1: %d\n", ret); + return 0; + } + + fc_rate = ub953_get_fc_rate(priv); + + if (priv->hw_data->is_ub971) { + mul = ctrl0 & 0x1f; + div = ctrl1; + + if (div == 0) + return 0; + + rate = div_u64(fc_rate * mul, 8 * div); + + dev_dbg(dev, "clkout: fc rate %llu, mul %u, div %u = %llu\n", + fc_rate, mul, div, rate); + } else { + mul = ctrl0 & 0x1f; + hs_clk_div = 1 << (ctrl0 >> 5); + div = ctrl1; + + if (div == 0) + return 0; + + rate = div_u64(div_u64(fc_rate, hs_clk_div) * mul, div); + + dev_dbg(dev, + "clkout: fc rate %llu, hs_clk_div %u, mul %u, div %u = %llu\n", + fc_rate, hs_clk_div, mul, div, rate); + } + + return rate; +} + +static long ub953_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct ub953_clkout_data clkout_data; + + ub953_calc_clkout_params(priv, rate, &clkout_data); + + return clkout_data.rate; +} + +static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw); + struct ub953_clkout_data clkout_data; + + ub953_calc_clkout_params(priv, rate, &clkout_data); + + dev_dbg(&priv->client->dev, "%s %lu (requested %lu)\n", __func__, + clkout_data.rate, rate); + + ub953_write_clkout_regs(priv, &clkout_data); + + return 0; +} + +static const struct clk_ops ub953_clkout_ops = { + .recalc_rate = ub953_clkout_recalc_rate, + .round_rate = ub953_clkout_round_rate, + .set_rate = ub953_clkout_set_rate, +}; + +static int ub953_register_clkout(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + const struct clk_init_data init = { + .name = kasprintf(GFP_KERNEL, "ds90%s.%s.clk_out", + priv->hw_data->model, dev_name(dev)), + .ops = &ub953_clkout_ops, + }; + struct ub953_clkout_data clkout_data; + int ret; + + if (!init.name) + return -ENOMEM; + + /* Initialize clkout to 25MHz by default */ + ub953_calc_clkout_params(priv, UB953_DEFAULT_CLKOUT_RATE, &clkout_data); + ub953_write_clkout_regs(priv, &clkout_data); + + priv->clkout_clk_hw.init = &init; + + ret = devm_clk_hw_register(dev, &priv->clkout_clk_hw); + kfree(init.name); + if (ret) + return dev_err_probe(dev, ret, "Cannot register clock HW\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->clkout_clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; +} + +static int ub953_add_i2c_adapter(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *i2c_handle; + int ret; + + i2c_handle = device_get_named_child_node(dev, "i2c"); + if (!i2c_handle) + return 0; + + ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, + dev, i2c_handle); + + fwnode_handle_put(i2c_handle); + + if (ret) + return ret; + + return 0; +} + +static const struct regmap_config ub953_regmap_config = { + .name = "ds90ub953", + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_DEFAULT, + .val_format_endian = REGMAP_ENDIAN_DEFAULT, +}; + +static int ub953_parse_dt(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep_fwnode; + unsigned char nlanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + UB953_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret) + return dev_err_probe(dev, ret, + "failed to parse sink endpoint data\n"); + + nlanes = vep.bus.mipi_csi2.num_data_lanes; + if (nlanes != 1 && nlanes != 2 && nlanes != 4) + return dev_err_probe(dev, -EINVAL, + "bad number of data-lanes: %u\n", nlanes); + + priv->num_data_lanes = nlanes; + + priv->non_continous_clk = vep.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + return 0; +} + +static int ub953_hw_init(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + bool mode_override; + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_MODE_SEL, &v); + if (ret) + return ret; + + if (!(v & UB953_REG_MODE_SEL_MODE_DONE)) + return dev_err_probe(dev, -EIO, "Mode value not stabilized\n"); + + mode_override = v & UB953_REG_MODE_SEL_MODE_OVERRIDE; + + switch (v & UB953_REG_MODE_SEL_MODE_MASK) { + case 0: + priv->mode = UB953_MODE_SYNC; + break; + case 2: + priv->mode = UB953_MODE_NONSYNC_EXT; + break; + case 3: + priv->mode = UB953_MODE_NONSYNC_INT; + break; + case 5: + priv->mode = UB953_MODE_DVP; + break; + default: + return dev_err_probe(dev, -EIO, + "Invalid mode in mode register\n"); + } + + dev_dbg(dev, "mode from %s: %#x\n", mode_override ? "reg" : "strap", + priv->mode); + + if (priv->mode != UB953_MODE_SYNC && + priv->mode != UB953_MODE_NONSYNC_EXT) + return dev_err_probe(dev, -ENODEV, + "Unsupported mode selected: %u\n", + priv->mode); + + if (priv->mode == UB953_MODE_NONSYNC_EXT && !priv->clkin) + return dev_err_probe(dev, -EINVAL, + "clkin required for non-sync ext mode\n"); + + ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &v); + if (ret) + return dev_err_probe(dev, ret, "Failed to read revision"); + + dev_info(dev, "Found %s rev/mask %#04x\n", priv->hw_data->model, v); + + ret = ub953_read(priv, UB953_REG_GENERAL_CFG, &v); + if (ret) + return ret; + + dev_dbg(dev, "i2c strap setting %s V\n", + (v & UB953_REG_GENERAL_CFG_I2C_STRAP_MODE) ? "1.8" : "3.3"); + + ret = ub953_i2c_master_init(priv); + if (ret) + return dev_err_probe(dev, ret, "i2c init failed\n"); + + ub953_write(priv, UB953_REG_GENERAL_CFG, + (priv->non_continous_clk ? 0 : UB953_REG_GENERAL_CFG_CONT_CLK) | + ((priv->num_data_lanes - 1) << UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT) | + UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE); + + return 0; +} + +static int ub953_subdev_init(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub953_entity_ops; + + priv->pads[0].flags = MEDIA_PAD_FL_SINK; + priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub953_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub953_v4l2_notifier_unregister(priv); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void ub953_subdev_uninit(struct ub953_data *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + ub953_v4l2_notifier_unregister(priv); + v4l2_subdev_cleanup(&priv->sd); + fwnode_handle_put(priv->sd.fwnode); + media_entity_cleanup(&priv->sd.entity); +} + +static int ub953_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub953_data *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->hw_data = device_get_match_data(dev); + + priv->plat_data = dev_get_platdata(&client->dev); + if (!priv->plat_data) + return dev_err_probe(dev, -ENODEV, "Platform data missing\n"); + + mutex_init(&priv->reg_lock); + + /* + * Initialize to invalid values so that the first reg writes will + * configure the target. + */ + priv->current_indirect_target = 0xff; + + priv->regmap = devm_regmap_init_i2c(client, &ub953_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err_probe(dev, ret, "Failed to init regmap\n"); + goto err_mutex_destroy; + } + + priv->clkin = devm_clk_get_optional(dev, "clkin"); + if (IS_ERR(priv->clkin)) { + ret = PTR_ERR(priv->clkin); + dev_err_probe(dev, ret, "failed to parse 'clkin'\n"); + goto err_mutex_destroy; + } + + ret = ub953_parse_dt(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub953_hw_init(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub953_gpiochip_probe(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to init gpiochip\n"); + goto err_mutex_destroy; + } + + ret = ub953_register_clkout(priv); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clkout\n"); + goto err_gpiochip_remove; + } + + ret = ub953_subdev_init(priv); + if (ret) + goto err_gpiochip_remove; + + ret = ub953_add_i2c_adapter(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + goto err_subdev_uninit; + } + + return 0; + +err_subdev_uninit: + ub953_subdev_uninit(priv); +err_gpiochip_remove: + ub953_gpiochip_remove(priv); +err_mutex_destroy: + mutex_destroy(&priv->reg_lock); + + return ret; +} + +static void ub953_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub953_data *priv = sd_to_ub953(sd); + + i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port); + + ub953_subdev_uninit(priv); + + ub953_gpiochip_remove(priv); + mutex_destroy(&priv->reg_lock); +} + +static const struct ub953_hw_data ds90ub953_hw = { + .model = "ub953", +}; + +static const struct ub953_hw_data ds90ub971_hw = { + .model = "ub971", + .is_ub971 = true, +}; + +static const struct i2c_device_id ub953_id[] = { + { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw }, + { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ub953_id); + +static const struct of_device_id ub953_dt_ids[] = { + { .compatible = "ti,ds90ub953-q1", .data = &ds90ub953_hw }, + { .compatible = "ti,ds90ub971-q1", .data = &ds90ub971_hw }, + {} +}; +MODULE_DEVICE_TABLE(of, ub953_dt_ids); + +static struct i2c_driver ds90ub953_driver = { + .probe = ub953_probe, + .remove = ub953_remove, + .id_table = ub953_id, + .driver = { + .name = "ds90ub953", + .of_match_table = ub953_dt_ids, + }, +}; +module_i2c_driver(ds90ub953_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV CSI-2 Serializers Driver"); +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c new file mode 100644 index 000000000000..8ba5750f5a23 --- /dev/null +++ b/drivers/media/i2c/ds90ub960.c @@ -0,0 +1,4059 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB960-Q1 video deserializer + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/* + * (Possible) TODOs: + * + * - PM for serializer and remote peripherals. We need to manage: + * - VPOC + * - Power domain? Regulator? Somehow any remote device should be able to + * cause the VPOC to be turned on. + * - Link between the deserializer and the serializer + * - Related to VPOC management. We probably always want to turn on the VPOC + * and then enable the link. + * - Serializer's services: i2c, gpios, power + * - The serializer needs to resume before the remote peripherals can + * e.g. use the i2c. + * - How to handle gpios? Reserving a gpio essentially keeps the provider + * (serializer) always powered on. + * - Do we need a new bus for the FPD-Link? At the moment the serializers + * are children of the same i2c-adapter where the deserializer resides. + * - i2c-atr could be made embeddable instead of allocatable. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/fwnode.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c-atr.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include <media/i2c/ds90ub9xx.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define MHZ(v) ((u32)((v) * 1000000U)) + +#define UB960_POLL_TIME_MS 500 + +#define UB960_MAX_RX_NPORTS 4 +#define UB960_MAX_TX_NPORTS 2 +#define UB960_MAX_NPORTS (UB960_MAX_RX_NPORTS + UB960_MAX_TX_NPORTS) + +#define UB960_MAX_PORT_ALIASES 8 + +#define UB960_NUM_BC_GPIOS 4 + +/* + * Register map + * + * 0x00-0x32 Shared (UB960_SR) + * 0x33-0x3a CSI-2 TX (per-port paged on DS90UB960, shared on 954) (UB960_TR) + * 0x4c Shared (UB960_SR) + * 0x4d-0x7f FPD-Link RX, per-port paged (UB960_RR) + * 0xb0-0xbf Shared (UB960_SR) + * 0xd0-0xdf FPD-Link RX, per-port paged (UB960_RR) + * 0xf0-0xf5 Shared (UB960_SR) + * 0xf8-0xfb Shared (UB960_SR) + * All others Reserved + * + * Register prefixes: + * UB960_SR_* = Shared register + * UB960_RR_* = FPD-Link RX, per-port paged register + * UB960_TR_* = CSI-2 TX, per-port paged register + * UB960_XR_* = Reserved register + * UB960_IR_* = Indirect register + */ + +#define UB960_SR_I2C_DEV_ID 0x00 +#define UB960_SR_RESET 0x01 +#define UB960_SR_RESET_DIGITAL_RESET1 BIT(1) +#define UB960_SR_RESET_DIGITAL_RESET0 BIT(0) +#define UB960_SR_RESET_GPIO_LOCK_RELEASE BIT(5) + +#define UB960_SR_GEN_CONFIG 0x02 +#define UB960_SR_REV_MASK 0x03 +#define UB960_SR_DEVICE_STS 0x04 +#define UB960_SR_PAR_ERR_THOLD_HI 0x05 +#define UB960_SR_PAR_ERR_THOLD_LO 0x06 +#define UB960_SR_BCC_WDOG_CTL 0x07 +#define UB960_SR_I2C_CTL1 0x08 +#define UB960_SR_I2C_CTL2 0x09 +#define UB960_SR_SCL_HIGH_TIME 0x0a +#define UB960_SR_SCL_LOW_TIME 0x0b +#define UB960_SR_RX_PORT_CTL 0x0c +#define UB960_SR_IO_CTL 0x0d +#define UB960_SR_GPIO_PIN_STS 0x0e +#define UB960_SR_GPIO_INPUT_CTL 0x0f +#define UB960_SR_GPIO_PIN_CTL(n) (0x10 + (n)) /* n < UB960_NUM_GPIOS */ +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SEL 5 +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SRC_SHIFT 2 +#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_EN BIT(0) + +#define UB960_SR_FS_CTL 0x18 +#define UB960_SR_FS_HIGH_TIME_1 0x19 +#define UB960_SR_FS_HIGH_TIME_0 0x1a +#define UB960_SR_FS_LOW_TIME_1 0x1b +#define UB960_SR_FS_LOW_TIME_0 0x1c +#define UB960_SR_MAX_FRM_HI 0x1d +#define UB960_SR_MAX_FRM_LO 0x1e +#define UB960_SR_CSI_PLL_CTL 0x1f + +#define UB960_SR_FWD_CTL1 0x20 +#define UB960_SR_FWD_CTL1_PORT_DIS(n) BIT((n) + 4) + +#define UB960_SR_FWD_CTL2 0x21 +#define UB960_SR_FWD_STS 0x22 + +#define UB960_SR_INTERRUPT_CTL 0x23 +#define UB960_SR_INTERRUPT_CTL_INT_EN BIT(7) +#define UB960_SR_INTERRUPT_CTL_IE_CSI_TX0 BIT(4) +#define UB960_SR_INTERRUPT_CTL_IE_RX(n) BIT((n)) /* rxport[n] IRQ */ + +#define UB960_SR_INTERRUPT_STS 0x24 +#define UB960_SR_INTERRUPT_STS_INT BIT(7) +#define UB960_SR_INTERRUPT_STS_IS_CSI_TX(n) BIT(4 + (n)) /* txport[n] IRQ */ +#define UB960_SR_INTERRUPT_STS_IS_RX(n) BIT((n)) /* rxport[n] IRQ */ + +#define UB960_SR_TS_CONFIG 0x25 +#define UB960_SR_TS_CONTROL 0x26 +#define UB960_SR_TS_LINE_HI 0x27 +#define UB960_SR_TS_LINE_LO 0x28 +#define UB960_SR_TS_STATUS 0x29 +#define UB960_SR_TIMESTAMP_P0_HI 0x2a +#define UB960_SR_TIMESTAMP_P0_LO 0x2b +#define UB960_SR_TIMESTAMP_P1_HI 0x2c +#define UB960_SR_TIMESTAMP_P1_LO 0x2d + +#define UB960_SR_CSI_PORT_SEL 0x32 + +#define UB960_TR_CSI_CTL 0x33 +#define UB960_TR_CSI_CTL_CSI_CAL_EN BIT(6) +#define UB960_TR_CSI_CTL_CSI_CONTS_CLOCK BIT(1) +#define UB960_TR_CSI_CTL_CSI_ENABLE BIT(0) + +#define UB960_TR_CSI_CTL2 0x34 +#define UB960_TR_CSI_STS 0x35 +#define UB960_TR_CSI_TX_ICR 0x36 + +#define UB960_TR_CSI_TX_ISR 0x37 +#define UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR BIT(3) +#define UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR BIT(1) + +#define UB960_TR_CSI_TEST_CTL 0x38 +#define UB960_TR_CSI_TEST_PATT_HI 0x39 +#define UB960_TR_CSI_TEST_PATT_LO 0x3a + +#define UB960_XR_SFILTER_CFG 0x41 +#define UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT 4 +#define UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT 0 + +#define UB960_XR_AEQ_CTL1 0x42 +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_FPD_CLK BIT(6) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_ENCODING BIT(5) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_PARITY BIT(4) +#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK \ + (UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_FPD_CLK | \ + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_ENCODING | \ + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_PARITY) +#define UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN BIT(0) + +#define UB960_XR_AEQ_ERR_THOLD 0x43 + +#define UB960_RR_BCC_ERR_CTL 0x46 +#define UB960_RR_BCC_STATUS 0x47 +#define UB960_RR_BCC_STATUS_SEQ_ERROR BIT(5) +#define UB960_RR_BCC_STATUS_MASTER_ERR BIT(4) +#define UB960_RR_BCC_STATUS_MASTER_TO BIT(3) +#define UB960_RR_BCC_STATUS_SLAVE_ERR BIT(2) +#define UB960_RR_BCC_STATUS_SLAVE_TO BIT(1) +#define UB960_RR_BCC_STATUS_RESP_ERR BIT(0) +#define UB960_RR_BCC_STATUS_ERROR_MASK \ + (UB960_RR_BCC_STATUS_SEQ_ERROR | UB960_RR_BCC_STATUS_MASTER_ERR | \ + UB960_RR_BCC_STATUS_MASTER_TO | UB960_RR_BCC_STATUS_SLAVE_ERR | \ + UB960_RR_BCC_STATUS_SLAVE_TO | UB960_RR_BCC_STATUS_RESP_ERR) + +#define UB960_RR_FPD3_CAP 0x4a +#define UB960_RR_RAW_EMBED_DTYPE 0x4b +#define UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT 6 + +#define UB960_SR_FPD3_PORT_SEL 0x4c + +#define UB960_RR_RX_PORT_STS1 0x4d +#define UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS1_LOCK_STS_CHG BIT(4) +#define UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS1_PARITY_ERROR BIT(2) +#define UB960_RR_RX_PORT_STS1_PORT_PASS BIT(1) +#define UB960_RR_RX_PORT_STS1_LOCK_STS BIT(0) +#define UB960_RR_RX_PORT_STS1_ERROR_MASK \ + (UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR | \ + UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR | \ + UB960_RR_RX_PORT_STS1_PARITY_ERROR) + +#define UB960_RR_RX_PORT_STS2 0x4e +#define UB960_RR_RX_PORT_STS2_LINE_LEN_UNSTABLE BIT(7) +#define UB960_RR_RX_PORT_STS2_LINE_LEN_CHG BIT(6) +#define UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS2_BUFFER_ERROR BIT(4) +#define UB960_RR_RX_PORT_STS2_CSI_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS2_FREQ_STABLE BIT(2) +#define UB960_RR_RX_PORT_STS2_CABLE_FAULT BIT(1) +#define UB960_RR_RX_PORT_STS2_LINE_CNT_CHG BIT(0) +#define UB960_RR_RX_PORT_STS2_ERROR_MASK \ + UB960_RR_RX_PORT_STS2_BUFFER_ERROR + +#define UB960_RR_RX_FREQ_HIGH 0x4f +#define UB960_RR_RX_FREQ_LOW 0x50 +#define UB960_RR_SENSOR_STS_0 0x51 +#define UB960_RR_SENSOR_STS_1 0x52 +#define UB960_RR_SENSOR_STS_2 0x53 +#define UB960_RR_SENSOR_STS_3 0x54 +#define UB960_RR_RX_PAR_ERR_HI 0x55 +#define UB960_RR_RX_PAR_ERR_LO 0x56 +#define UB960_RR_BIST_ERR_COUNT 0x57 + +#define UB960_RR_BCC_CONFIG 0x58 +#define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) +#define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK GENMASK(2, 0) + +#define UB960_RR_DATAPATH_CTL1 0x59 +#define UB960_RR_DATAPATH_CTL2 0x5a +#define UB960_RR_SER_ID 0x5b +#define UB960_RR_SER_ALIAS_ID 0x5c + +/* For these two register sets: n < UB960_MAX_PORT_ALIASES */ +#define UB960_RR_SLAVE_ID(n) (0x5d + (n)) +#define UB960_RR_SLAVE_ALIAS(n) (0x65 + (n)) + +#define UB960_RR_PORT_CONFIG 0x6d +#define UB960_RR_PORT_CONFIG_FPD3_MODE_MASK GENMASK(1, 0) + +#define UB960_RR_BC_GPIO_CTL(n) (0x6e + (n)) /* n < 2 */ +#define UB960_RR_RAW10_ID 0x70 +#define UB960_RR_RAW10_ID_VC_SHIFT 6 +#define UB960_RR_RAW10_ID_DT_SHIFT 0 + +#define UB960_RR_RAW12_ID 0x71 +#define UB960_RR_CSI_VC_MAP 0x72 +#define UB960_RR_CSI_VC_MAP_SHIFT(x) ((x) * 2) + +#define UB960_RR_LINE_COUNT_HI 0x73 +#define UB960_RR_LINE_COUNT_LO 0x74 +#define UB960_RR_LINE_LEN_1 0x75 +#define UB960_RR_LINE_LEN_0 0x76 +#define UB960_RR_FREQ_DET_CTL 0x77 +#define UB960_RR_MAILBOX_1 0x78 +#define UB960_RR_MAILBOX_2 0x79 + +#define UB960_RR_CSI_RX_STS 0x7a +#define UB960_RR_CSI_RX_STS_LENGTH_ERR BIT(3) +#define UB960_RR_CSI_RX_STS_CKSUM_ERR BIT(2) +#define UB960_RR_CSI_RX_STS_ECC2_ERR BIT(1) +#define UB960_RR_CSI_RX_STS_ECC1_ERR BIT(0) +#define UB960_RR_CSI_RX_STS_ERROR_MASK \ + (UB960_RR_CSI_RX_STS_LENGTH_ERR | UB960_RR_CSI_RX_STS_CKSUM_ERR | \ + UB960_RR_CSI_RX_STS_ECC2_ERR | UB960_RR_CSI_RX_STS_ECC1_ERR) + +#define UB960_RR_CSI_ERR_COUNTER 0x7b +#define UB960_RR_PORT_CONFIG2 0x7c +#define UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK GENMASK(7, 6) +#define UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT 6 + +#define UB960_RR_PORT_CONFIG2_LV_POL_LOW BIT(1) +#define UB960_RR_PORT_CONFIG2_FV_POL_LOW BIT(0) + +#define UB960_RR_PORT_PASS_CTL 0x7d +#define UB960_RR_SEN_INT_RISE_CTL 0x7e +#define UB960_RR_SEN_INT_FALL_CTL 0x7f + +#define UB960_SR_CSI_FRAME_COUNT_HI(n) (0x90 + 8 * (n)) +#define UB960_SR_CSI_FRAME_COUNT_LO(n) (0x91 + 8 * (n)) +#define UB960_SR_CSI_FRAME_ERR_COUNT_HI(n) (0x92 + 8 * (n)) +#define UB960_SR_CSI_FRAME_ERR_COUNT_LO(n) (0x93 + 8 * (n)) +#define UB960_SR_CSI_LINE_COUNT_HI(n) (0x94 + 8 * (n)) +#define UB960_SR_CSI_LINE_COUNT_LO(n) (0x95 + 8 * (n)) +#define UB960_SR_CSI_LINE_ERR_COUNT_HI(n) (0x96 + 8 * (n)) +#define UB960_SR_CSI_LINE_ERR_COUNT_LO(n) (0x97 + 8 * (n)) + +#define UB960_XR_REFCLK_FREQ 0xa5 /* UB960 */ + +#define UB960_RR_VC_ID_MAP(x) (0xa0 + (x)) /* UB9702 */ + +#define UB960_SR_IND_ACC_CTL 0xb0 +#define UB960_SR_IND_ACC_CTL_IA_AUTO_INC BIT(1) + +#define UB960_SR_IND_ACC_ADDR 0xb1 +#define UB960_SR_IND_ACC_DATA 0xb2 +#define UB960_SR_BIST_CONTROL 0xb3 +#define UB960_SR_MODE_IDX_STS 0xb8 +#define UB960_SR_LINK_ERROR_COUNT 0xb9 +#define UB960_SR_FPD3_ENC_CTL 0xba +#define UB960_SR_FV_MIN_TIME 0xbc +#define UB960_SR_GPIO_PD_CTL 0xbe + +#define UB960_SR_FPD_RATE_CFG 0xc2 /* UB9702 */ +#define UB960_SR_CSI_PLL_DIV 0xc9 /* UB9702 */ + +#define UB960_RR_PORT_DEBUG 0xd0 +#define UB960_RR_AEQ_CTL2 0xd2 +#define UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR BIT(2) + +#define UB960_RR_AEQ_STATUS 0xd3 +#define UB960_RR_AEQ_STATUS_STATUS_2 GENMASK(5, 3) +#define UB960_RR_AEQ_STATUS_STATUS_1 GENMASK(2, 0) + +#define UB960_RR_AEQ_BYPASS 0xd4 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT 5 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK GENMASK(7, 5) +#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT 1 +#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK GENMASK(3, 1) +#define UB960_RR_AEQ_BYPASS_ENABLE BIT(0) + +#define UB960_RR_AEQ_MIN_MAX 0xd5 +#define UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT 4 +#define UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT 0 + +#define UB960_RR_SFILTER_STS_0 0xd6 +#define UB960_RR_SFILTER_STS_1 0xd7 +#define UB960_RR_PORT_ICR_HI 0xd8 +#define UB960_RR_PORT_ICR_LO 0xd9 +#define UB960_RR_PORT_ISR_HI 0xda +#define UB960_RR_PORT_ISR_LO 0xdb +#define UB960_RR_FC_GPIO_STS 0xdc +#define UB960_RR_FC_GPIO_ICR 0xdd +#define UB960_RR_SEN_INT_RISE_STS 0xde +#define UB960_RR_SEN_INT_FALL_STS 0xdf + +#define UB960_RR_CHANNEL_MODE 0xe4 /* UB9702 */ + +#define UB960_SR_FPD3_RX_ID(n) (0xf0 + (n)) +#define UB960_SR_FPD3_RX_ID_LEN 6 + +#define UB960_SR_I2C_RX_ID(n) (0xf8 + (n)) /* < UB960_FPD_RX_NPORTS */ + +/* Indirect register blocks */ +#define UB960_IND_TARGET_PAT_GEN 0x00 +#define UB960_IND_TARGET_RX_ANA(n) (0x01 + (n)) +#define UB960_IND_TARGET_CSI_CSIPLL_REG_1 0x92 /* UB9702 */ +#define UB960_IND_TARGET_CSI_ANA 0x07 + +/* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ + +#define UB960_IR_PGEN_CTL 0x01 +#define UB960_IR_PGEN_CTL_PGEN_ENABLE BIT(0) + +#define UB960_IR_PGEN_CFG 0x02 +#define UB960_IR_PGEN_CSI_DI 0x03 +#define UB960_IR_PGEN_LINE_SIZE1 0x04 +#define UB960_IR_PGEN_LINE_SIZE0 0x05 +#define UB960_IR_PGEN_BAR_SIZE1 0x06 +#define UB960_IR_PGEN_BAR_SIZE0 0x07 +#define UB960_IR_PGEN_ACT_LPF1 0x08 +#define UB960_IR_PGEN_ACT_LPF0 0x09 +#define UB960_IR_PGEN_TOT_LPF1 0x0a +#define UB960_IR_PGEN_TOT_LPF0 0x0b +#define UB960_IR_PGEN_LINE_PD1 0x0c +#define UB960_IR_PGEN_LINE_PD0 0x0d +#define UB960_IR_PGEN_VBP 0x0e +#define UB960_IR_PGEN_VFP 0x0f +#define UB960_IR_PGEN_COLOR(n) (0x10 + (n)) /* n < 15 */ + +#define UB960_IR_RX_ANA_STROBE_SET_CLK 0x08 +#define UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY BIT(3) +#define UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK GENMASK(2, 0) + +#define UB960_IR_RX_ANA_STROBE_SET_DATA 0x09 +#define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY BIT(3) +#define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK GENMASK(2, 0) + +/* EQ related */ + +#define UB960_MIN_AEQ_STROBE_POS -7 +#define UB960_MAX_AEQ_STROBE_POS 7 + +#define UB960_MANUAL_STROBE_EXTRA_DELAY 6 + +#define UB960_MIN_MANUAL_STROBE_POS -(7 + UB960_MANUAL_STROBE_EXTRA_DELAY) +#define UB960_MAX_MANUAL_STROBE_POS (7 + UB960_MANUAL_STROBE_EXTRA_DELAY) +#define UB960_NUM_MANUAL_STROBE_POS (UB960_MAX_MANUAL_STROBE_POS - UB960_MIN_MANUAL_STROBE_POS + 1) + +#define UB960_MIN_EQ_LEVEL 0 +#define UB960_MAX_EQ_LEVEL 14 +#define UB960_NUM_EQ_LEVELS (UB960_MAX_EQ_LEVEL - UB960_MIN_EQ_LEVEL + 1) + +struct ub960_hw_data { + const char *model; + u8 num_rxports; + u8 num_txports; + bool is_ub9702; + bool is_fpdlink4; +}; + +enum ub960_rxport_mode { + RXPORT_MODE_RAW10 = 0, + RXPORT_MODE_RAW12_HF = 1, + RXPORT_MODE_RAW12_LF = 2, + RXPORT_MODE_CSI2_SYNC = 3, + RXPORT_MODE_CSI2_NONSYNC = 4, + RXPORT_MODE_LAST = RXPORT_MODE_CSI2_NONSYNC, +}; + +enum ub960_rxport_cdr { + RXPORT_CDR_FPD3 = 0, + RXPORT_CDR_FPD4 = 1, + RXPORT_CDR_LAST = RXPORT_CDR_FPD4, +}; + +struct ub960_rxport { + struct ub960_data *priv; + u8 nport; /* RX port number, and index in priv->rxport[] */ + + struct { + struct v4l2_subdev *sd; + u16 pad; + struct fwnode_handle *ep_fwnode; + } source; + + /* Serializer */ + struct { + struct fwnode_handle *fwnode; + struct i2c_client *client; + unsigned short alias; /* I2C alias (lower 7 bits) */ + struct ds90ub9xx_platform_data pdata; + } ser; + + enum ub960_rxport_mode rx_mode; + enum ub960_rxport_cdr cdr_mode; + + u8 lv_fv_pol; /* LV and FV polarities */ + + struct regulator *vpoc; + + /* EQ settings */ + struct { + bool manual_eq; + + s8 strobe_pos; + + union { + struct { + u8 eq_level_min; + u8 eq_level_max; + } aeq; + + struct { + u8 eq_level; + } manual; + }; + } eq; + + const struct i2c_client *aliased_clients[UB960_MAX_PORT_ALIASES]; +}; + +struct ub960_asd { + struct v4l2_async_connection base; + struct ub960_rxport *rxport; +}; + +static inline struct ub960_asd *to_ub960_asd(struct v4l2_async_connection *asd) +{ + return container_of(asd, struct ub960_asd, base); +} + +struct ub960_txport { + struct ub960_data *priv; + u8 nport; /* TX port number, and index in priv->txport[] */ + + u32 num_data_lanes; + bool non_continous_clk; +}; + +struct ub960_data { + const struct ub960_hw_data *hw_data; + struct i2c_client *client; /* for shared local registers */ + struct regmap *regmap; + + /* lock for register access */ + struct mutex reg_lock; + + struct clk *refclk; + + struct regulator *vddio; + + struct gpio_desc *pd_gpio; + struct delayed_work poll_work; + struct ub960_rxport *rxports[UB960_MAX_RX_NPORTS]; + struct ub960_txport *txports[UB960_MAX_TX_NPORTS]; + + struct v4l2_subdev sd; + struct media_pad pads[UB960_MAX_NPORTS]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + + u32 tx_data_rate; /* Nominal data rate (Gb/s) */ + s64 tx_link_freq[1]; + + struct i2c_atr *atr; + + struct { + u8 rxport; + u8 txport; + u8 indirect_target; + } reg_current; + + bool streaming; + + u8 stored_fwd_ctl; + + u64 stream_enable_mask[UB960_MAX_NPORTS]; + + /* These are common to all ports */ + struct { + bool manual; + + s8 min; + s8 max; + } strobe; +}; + +static inline struct ub960_data *sd_to_ub960(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub960_data, sd); +} + +static inline bool ub960_pad_is_sink(struct ub960_data *priv, u32 pad) +{ + return pad < priv->hw_data->num_rxports; +} + +static inline bool ub960_pad_is_source(struct ub960_data *priv, u32 pad) +{ + return pad >= priv->hw_data->num_rxports; +} + +static inline unsigned int ub960_pad_to_port(struct ub960_data *priv, u32 pad) +{ + if (ub960_pad_is_sink(priv, pad)) + return pad; + else + return pad - priv->hw_data->num_rxports; +} + +struct ub960_format_info { + u32 code; + u32 bpp; + u8 datatype; + bool meta; +}; + +static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = MIPI_CSI2_DT_YUV422_8B, }, + + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .datatype = MIPI_CSI2_DT_RAW12, }, +}; + +static const struct ub960_format_info *ub960_find_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ub960_formats); i++) { + if (ub960_formats[i].code == code) + return &ub960_formats[i]; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Basic device access + */ + +static int ub960_read(struct ub960_data *priv, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_write(struct ub960_data *priv, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_update_bits(struct ub960_data *priv, u8 reg, u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_read16(struct ub960_data *priv, u8 reg, u16 *val) +{ + struct device *dev = &priv->client->dev; + __be16 __v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = be16_to_cpu(__v); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.rxport == nport) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_FPD3_PORT_SEL, + (nport << 4) | BIT(nport)); + if (ret) { + dev_err(dev, "%s: cannot select rxport %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->reg_current.rxport = nport; + + return 0; +} + +static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_rxport_read16(struct ub960_data *priv, u8 nport, u8 reg, + u16 *val) +{ + struct device *dev = &priv->client->dev; + __be16 __v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_rxport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_bulk_read(priv->regmap, reg, &__v, sizeof(__v)); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = be16_to_cpu(__v); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.txport == nport) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_CSI_PORT_SEL, + (nport << 4) | BIT(nport)); + if (ret) { + dev_err(dev, "%s: cannot select tx port %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->reg_current.txport = nport; + + return 0; +} + +static int ub960_txport_read(struct ub960_data *priv, u8 nport, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_write(struct ub960_data *priv, u8 nport, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_txport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_txport_select(priv, nport); + if (ret) + goto out_unlock; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_select_ind_reg_block(struct ub960_data *priv, u8 block) +{ + struct device *dev = &priv->client->dev; + int ret; + + lockdep_assert_held(&priv->reg_lock); + + if (priv->reg_current.indirect_target == block) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_CTL, block << 2); + if (ret) { + dev_err(dev, "%s: cannot select indirect target %u (%d)!\n", + __func__, block, ret); + return ret; + } + + priv->reg_current.indirect_target = block; + + return 0; +} + +static int ub960_read_ind(struct ub960_data *priv, u8 block, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_read(priv->regmap, UB960_SR_IND_ACC_DATA, &v); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when reading %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + *val = v; + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_write_ind(struct ub960_data *priv, u8 block, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_DATA, val); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when writing %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +static int ub960_ind_update_bits(struct ub960_data *priv, u8 block, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + mutex_lock(&priv->reg_lock); + + ret = ub960_select_ind_reg_block(priv, block); + if (ret) + goto out_unlock; + + ret = regmap_write(priv->regmap, UB960_SR_IND_ACC_ADDR, reg); + if (ret) { + dev_err(dev, + "Write to IND_ACC_ADDR failed when updating %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + + ret = regmap_update_bits(priv->regmap, UB960_SR_IND_ACC_DATA, mask, + val); + if (ret) { + dev_err(dev, + "Write to IND_ACC_DATA failed when updating %u:%x02x: %d\n", + block, reg, ret); + goto out_unlock; + } + +out_unlock: + mutex_unlock(&priv->reg_lock); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C-ATR (address translator) + */ + +static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client, u16 alias) +{ + struct ub960_data *priv = i2c_atr_get_driver_data(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { + if (!rxport->aliased_clients[reg_idx]) + break; + } + + if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + dev_err(dev, "rx%u: alias pool exhausted\n", rxport->nport); + return -EADDRNOTAVAIL; + } + + rxport->aliased_clients[reg_idx] = client; + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), + client->addr << 1); + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), + alias << 1); + + dev_dbg(dev, "rx%u: client 0x%02x assigned alias 0x%02x at slot %u\n", + rxport->nport, client->addr, alias, reg_idx); + + return 0; +} + +static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client) +{ + struct ub960_data *priv = i2c_atr_get_driver_data(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + + for (reg_idx = 0; reg_idx < ARRAY_SIZE(rxport->aliased_clients); reg_idx++) { + if (rxport->aliased_clients[reg_idx] == client) + break; + } + + if (reg_idx == ARRAY_SIZE(rxport->aliased_clients)) { + dev_err(dev, "rx%u: client 0x%02x is not mapped!\n", + rxport->nport, client->addr); + return; + } + + rxport->aliased_clients[reg_idx] = NULL; + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); + + dev_dbg(dev, "rx%u: client 0x%02x released at slot %u\n", rxport->nport, + client->addr, reg_idx); +} + +static const struct i2c_atr_ops ub960_atr_ops = { + .attach_client = ub960_atr_attach_client, + .detach_client = ub960_atr_detach_client, +}; + +static int ub960_init_atr(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + struct i2c_adapter *parent_adap = priv->client->adapter; + + priv->atr = i2c_atr_new(parent_adap, dev, &ub960_atr_ops, + priv->hw_data->num_rxports); + if (IS_ERR(priv->atr)) + return PTR_ERR(priv->atr); + + i2c_atr_set_driver_data(priv->atr, priv); + + return 0; +} + +static void ub960_uninit_atr(struct ub960_data *priv) +{ + i2c_atr_delete(priv->atr); + priv->atr = NULL; +} + +/* ----------------------------------------------------------------------------- + * TX ports + */ + +static int ub960_parse_dt_txport(struct ub960_data *priv, + struct fwnode_handle *ep_fwnode, + u8 nport) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = {}; + struct ub960_txport *txport; + int ret; + + txport = kzalloc(sizeof(*txport), GFP_KERNEL); + if (!txport) + return -ENOMEM; + + txport->priv = priv; + txport->nport = nport; + + vep.bus_type = V4L2_MBUS_CSI2_DPHY; + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &vep); + if (ret) { + dev_err(dev, "tx%u: failed to parse endpoint data\n", nport); + goto err_free_txport; + } + + txport->non_continous_clk = vep.bus.mipi_csi2.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + txport->num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + + if (vep.nr_of_link_frequencies != 1) { + ret = -EINVAL; + goto err_free_vep; + } + + priv->tx_link_freq[0] = vep.link_frequencies[0]; + priv->tx_data_rate = priv->tx_link_freq[0] * 2; + + if (priv->tx_data_rate != MHZ(1600) && + priv->tx_data_rate != MHZ(1200) && + priv->tx_data_rate != MHZ(800) && + priv->tx_data_rate != MHZ(400)) { + dev_err(dev, "tx%u: invalid 'link-frequencies' value\n", nport); + ret = -EINVAL; + goto err_free_vep; + } + + v4l2_fwnode_endpoint_free(&vep); + + priv->txports[nport] = txport; + + return 0; + +err_free_vep: + v4l2_fwnode_endpoint_free(&vep); +err_free_txport: + kfree(txport); + + return ret; +} + +static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 csi_tx_isr; + int ret; + + ret = ub960_txport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr); + if (ret) + return; + + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR) + dev_warn(dev, "TX%u: CSI_SYNC_ERROR\n", nport); + + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR) + dev_warn(dev, "TX%u: CSI_PASS_ERROR\n", nport); +} + +/* ----------------------------------------------------------------------------- + * RX ports + */ + +static int ub960_rxport_enable_vpocs(struct ub960_data *priv) +{ + unsigned int nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + ret = regulator_enable(rxport->vpoc); + if (ret) + goto err_disable_vpocs; + } + + return 0; + +err_disable_vpocs: + while (nport--) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + regulator_disable(rxport->vpoc); + } + + return ret; +} + +static void ub960_rxport_disable_vpocs(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->vpoc) + continue; + + regulator_disable(rxport->vpoc); + } +} + +static void ub960_rxport_clear_errors(struct ub960_data *priv, + unsigned int nport) +{ + u8 v; + + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &v); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &v); + + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PAR_ERR_LO, &v); + + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); +} + +static void ub960_clear_rx_errors(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) + ub960_rxport_clear_errors(priv, nport); +} + +static int ub960_rxport_get_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 *strobe_pos) +{ + u8 v; + u8 clk_delay, data_delay; + int ret; + + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, &v); + + clk_delay = (v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, &v); + + data_delay = (v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY) ? + 0 : UB960_MANUAL_STROBE_EXTRA_DELAY; + + ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v); + if (ret) + return ret; + + clk_delay += v & UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK; + + ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v); + if (ret) + return ret; + + data_delay += v & UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK; + + *strobe_pos = data_delay - clk_delay; + + return 0; +} + +static void ub960_rxport_set_strobe_pos(struct ub960_data *priv, + unsigned int nport, s8 strobe_pos) +{ + u8 clk_delay, data_delay; + + clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + if (strobe_pos < UB960_MIN_AEQ_STROBE_POS) + clk_delay = abs(strobe_pos) - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos > UB960_MAX_AEQ_STROBE_POS) + data_delay = strobe_pos - UB960_MANUAL_STROBE_EXTRA_DELAY; + else if (strobe_pos < 0) + clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY; + else if (strobe_pos > 0) + data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY; + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), + UB960_IR_RX_ANA_STROBE_SET_DATA, data_delay); +} + +static void ub960_rxport_set_strobe_range(struct ub960_data *priv, + s8 strobe_min, s8 strobe_max) +{ + /* Convert the signed strobe pos to positive zero based value */ + strobe_min -= UB960_MIN_AEQ_STROBE_POS; + strobe_max -= UB960_MIN_AEQ_STROBE_POS; + + ub960_write(priv, UB960_XR_SFILTER_CFG, + ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) | + ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT)); +} + +static int ub960_rxport_get_eq_level(struct ub960_data *priv, + unsigned int nport, u8 *eq_level) +{ + int ret; + u8 v; + + ret = ub960_rxport_read(priv, nport, UB960_RR_AEQ_STATUS, &v); + if (ret) + return ret; + + *eq_level = (v & UB960_RR_AEQ_STATUS_STATUS_1) + + (v & UB960_RR_AEQ_STATUS_STATUS_2); + + return 0; +} + +static void ub960_rxport_set_eq_level(struct ub960_data *priv, + unsigned int nport, u8 eq_level) +{ + u8 eq_stage_1_select_value, eq_stage_2_select_value; + const unsigned int eq_stage_max = 7; + u8 v; + + if (eq_level <= eq_stage_max) { + eq_stage_1_select_value = eq_level; + eq_stage_2_select_value = 0; + } else { + eq_stage_1_select_value = eq_stage_max; + eq_stage_2_select_value = eq_level - eq_stage_max; + } + + ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + + v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK | + UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK); + v |= eq_stage_1_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT; + v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT; + v |= UB960_RR_AEQ_BYPASS_ENABLE; + + ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v); +} + +static void ub960_rxport_set_eq_range(struct ub960_data *priv, + unsigned int nport, u8 eq_min, u8 eq_max) +{ + ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX, + (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) | + (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT)); + + /* Enable AEQ min setting */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR, + UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR); +} + +static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + /* We also set common settings here. Should be moved elsewhere. */ + + if (priv->strobe.manual) { + /* Disable AEQ_SFILTER_EN */ + ub960_update_bits(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0); + } else { + /* Enable SFILTER and error control */ + ub960_write(priv, UB960_XR_AEQ_CTL1, + UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_MASK | + UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN); + + /* Set AEQ strobe range */ + ub960_rxport_set_strobe_range(priv, priv->strobe.min, + priv->strobe.max); + } + + /* The rest are port specific */ + + if (priv->strobe.manual) + ub960_rxport_set_strobe_pos(priv, nport, rxport->eq.strobe_pos); + else + ub960_rxport_set_strobe_pos(priv, nport, 0); + + if (rxport->eq.manual_eq) { + ub960_rxport_set_eq_level(priv, nport, + rxport->eq.manual.eq_level); + + /* Enable AEQ Bypass */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, + UB960_RR_AEQ_BYPASS_ENABLE); + } else { + ub960_rxport_set_eq_range(priv, nport, + rxport->eq.aeq.eq_level_min, + rxport->eq.aeq.eq_level_max); + + /* Disable AEQ Bypass */ + ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS, + UB960_RR_AEQ_BYPASS_ENABLE, 0); + } +} + +static int ub960_rxport_link_ok(struct ub960_data *priv, unsigned int nport, + bool *ok) +{ + u8 rx_port_sts1, rx_port_sts2; + u16 parity_errors; + u8 csi_rx_sts; + u8 csi_err_cnt; + u8 bcc_sts; + int ret; + bool errors; + + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (ret) + return ret; + + if (!(rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS)) { + *ok = false; + return 0; + } + + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, + &rx_port_sts2); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &csi_rx_sts); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, + &csi_err_cnt); + if (ret) + return ret; + + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &bcc_sts); + if (ret) + return ret; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &parity_errors); + if (ret) + return ret; + + errors = (rx_port_sts1 & UB960_RR_RX_PORT_STS1_ERROR_MASK) || + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_ERROR_MASK) || + (bcc_sts & UB960_RR_BCC_STATUS_ERROR_MASK) || + (csi_rx_sts & UB960_RR_CSI_RX_STS_ERROR_MASK) || csi_err_cnt || + parity_errors; + + *ok = !errors; + + return 0; +} + +/* + * Wait for the RX ports to lock, have no errors and have stable strobe position + * and EQ level. + */ +static int ub960_rxport_wait_locks(struct ub960_data *priv, + unsigned long port_mask, + unsigned int *lock_mask) +{ + struct device *dev = &priv->client->dev; + unsigned long timeout; + unsigned int link_ok_mask; + unsigned int missing; + unsigned int loops; + u8 nport; + int ret; + + if (port_mask == 0) { + if (lock_mask) + *lock_mask = 0; + return 0; + } + + if (port_mask >= BIT(priv->hw_data->num_rxports)) + return -EINVAL; + + timeout = jiffies + msecs_to_jiffies(1000); + loops = 0; + link_ok_mask = 0; + + while (time_before(jiffies, timeout)) { + missing = 0; + + for_each_set_bit(nport, &port_mask, + priv->hw_data->num_rxports) { + struct ub960_rxport *rxport = priv->rxports[nport]; + bool ok; + + if (!rxport) + continue; + + ret = ub960_rxport_link_ok(priv, nport, &ok); + if (ret) + return ret; + + /* + * We want the link to be ok for two consecutive loops, + * as a link could get established just before our test + * and drop soon after. + */ + if (!ok || !(link_ok_mask & BIT(nport))) + missing++; + + if (ok) + link_ok_mask |= BIT(nport); + else + link_ok_mask &= ~BIT(nport); + } + + loops++; + + if (missing == 0) + break; + + msleep(50); + } + + if (lock_mask) + *lock_mask = link_ok_mask; + + dev_dbg(dev, "Wait locks done in %u loops\n", loops); + for_each_set_bit(nport, &port_mask, priv->hw_data->num_rxports) { + struct ub960_rxport *rxport = priv->rxports[nport]; + s8 strobe_pos, eq_level; + u16 v; + + if (!rxport) + continue; + + if (!(link_ok_mask & BIT(nport))) { + dev_dbg(dev, "\trx%u: not locked\n", nport); + continue; + } + + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v); + + ret = ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + if (ret) + return ret; + + ret = ub960_rxport_get_eq_level(priv, nport, &eq_level); + if (ret) + return ret; + + dev_dbg(dev, "\trx%u: locked, SP: %d, EQ: %u, freq %llu Hz\n", + nport, strobe_pos, eq_level, (v * 1000000ULL) >> 8); + } + + return 0; +} + +static unsigned long ub960_calc_bc_clk_rate_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int mult; + unsigned int div; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + mult = 1; + div = 10; + break; + + case RXPORT_MODE_CSI2_SYNC: + mult = 2; + div = 1; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + mult = 2; + div = 5; + break; + + default: + return 0; + } + + return clk_get_rate(priv->refclk) * mult / div; +} + +static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + return 2359400; + + case RXPORT_MODE_CSI2_SYNC: + return 47187500; + + case RXPORT_MODE_CSI2_NONSYNC: + return 9437500; + + default: + return 0; + } +} + +static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + struct device *dev = &priv->client->dev; + struct ds90ub9xx_platform_data *ser_pdata = &rxport->ser.pdata; + struct i2c_board_info ser_info = { + .of_node = to_of_node(rxport->ser.fwnode), + .fwnode = rxport->ser.fwnode, + .platform_data = ser_pdata, + }; + + ser_pdata->port = nport; + ser_pdata->atr = priv->atr; + if (priv->hw_data->is_ub9702) + ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub9702(priv, rxport); + else + ser_pdata->bc_rate = ub960_calc_bc_clk_rate_ub960(priv, rxport); + + /* + * The serializer is added under the same i2c adapter as the + * deserializer. This is not quite right, as the serializer is behind + * the FPD-Link. + */ + ser_info.addr = rxport->ser.alias; + rxport->ser.client = + i2c_new_client_device(priv->client->adapter, &ser_info); + if (IS_ERR(rxport->ser.client)) { + dev_err(dev, "rx%u: cannot add %s i2c device", nport, + ser_info.type); + return PTR_ERR(rxport->ser.client); + } + + dev_dbg(dev, "rx%u: remote serializer at alias 0x%02x (%u-%04x)\n", + nport, rxport->ser.client->addr, + rxport->ser.client->adapter->nr, rxport->ser.client->addr); + + return 0; +} + +static void ub960_rxport_remove_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + i2c_unregister_device(rxport->ser.client); + rxport->ser.client = NULL; +} + +/* Add serializer i2c devices for all initialized ports */ +static int ub960_rxport_add_serializers(struct ub960_data *priv) +{ + unsigned int nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ret = ub960_rxport_add_serializer(priv, nport); + if (ret) + goto err_remove_sers; + } + + return 0; + +err_remove_sers: + while (nport--) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ub960_rxport_remove_serializer(priv, nport); + } + + return ret; +} + +static void ub960_rxport_remove_serializers(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + ub960_rxport_remove_serializer(priv, nport); + } +} + +static void ub960_init_tx_port(struct ub960_data *priv, + struct ub960_txport *txport) +{ + unsigned int nport = txport->nport; + u8 csi_ctl = 0; + + /* + * From the datasheet: "initial CSI Skew-Calibration + * sequence [...] should be set when operating at 1.6 Gbps" + */ + if (priv->tx_data_rate == MHZ(1600)) + csi_ctl |= UB960_TR_CSI_CTL_CSI_CAL_EN; + + csi_ctl |= (4 - txport->num_data_lanes) << 4; + + if (!txport->non_continous_clk) + csi_ctl |= UB960_TR_CSI_CTL_CSI_CONTS_CLOCK; + + ub960_txport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl); +} + +static int ub960_init_tx_ports(struct ub960_data *priv) +{ + unsigned int nport; + u8 speed_select; + u8 pll_div; + + /* TX ports */ + + switch (priv->tx_data_rate) { + case MHZ(1600): + default: + speed_select = 0; + pll_div = 0x10; + break; + case MHZ(1200): + speed_select = 1; + pll_div = 0x18; + break; + case MHZ(800): + speed_select = 2; + pll_div = 0x10; + break; + case MHZ(400): + speed_select = 3; + pll_div = 0x10; + break; + } + + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select); + + if (priv->hw_data->is_ub9702) { + ub960_write(priv, UB960_SR_CSI_PLL_DIV, pll_div); + + switch (priv->tx_data_rate) { + case MHZ(1600): + default: + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x80); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + break; + case MHZ(800): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0x90); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4f, 0x2a); + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x4b, 0x2a); + break; + case MHZ(400): + ub960_write_ind(priv, UB960_IND_TARGET_CSI_ANA, 0x92, 0xa0); + break; + } + } + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + if (!txport) + continue; + + ub960_init_tx_port(priv, txport); + } + + return 0; +} + +static void ub960_init_rx_port_ub960(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u32 bc_freq_val; + + /* + * Back channel frequency select. + * Override FREQ_SELECT from the strap. + * 0 - 2.5 Mbps (DS90UB913A-Q1 / DS90UB933-Q1) + * 2 - 10 Mbps + * 6 - 50 Mbps (DS90UB953-Q1) + * + * Note that changing this setting will result in some errors on the back + * channel for a short period of time. + */ + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK, + bc_freq_val); + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, + UB960_RR_PORT_CONFIG_FPD3_MODE_MASK, + 0x3); + + /* + * RAW10_8BIT_CTL = 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, + UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_MASK, + 0x2 << UB960_RR_PORT_CONFIG2_RAW10_8BIT_CTL_SHIFT); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + return; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + /* CSI-2 Mode (DS90UB953-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, + 0x0); + + break; + } + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + rxport->lv_fv_pol); + + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + + /* Enable I2C communication to the serializer via the alias addr */ + ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, + rxport->ser.alias << 1); + + /* Configure EQ related settings */ + ub960_rxport_config_eq(priv, nport); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); +} + +static void ub960_init_rx_port_ub9702_fpd3(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u8 bc_freq_val; + u8 fpd_func_mode; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + bc_freq_val = 0; + fpd_func_mode = 5; + break; + + case RXPORT_MODE_RAW12_HF: + bc_freq_val = 0; + fpd_func_mode = 4; + break; + + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + fpd_func_mode = 6; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + fpd_func_mode = 2; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + fpd_func_mode = 2; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, + bc_freq_val); + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, fpd_func_mode); + + /* set serdes_eq_mode = 1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa8, 0x80); + + /* enable serdes driver */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x0d, 0x7f); + + /* set serdes_eq_offset=4 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + + /* init default serdes_eq_max in 0xa9 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xa9, 0x23); + + /* init serdes_eq_min in 0xaa */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xaa, 0); + + /* serdes_driver_ctl2 control: DS90UB953-Q1/DS90UB933-Q1/DS90UB913A-Q1 */ + ub960_ind_update_bits(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, + BIT(3), BIT(3)); + + /* RX port to half-rate */ + ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + BIT(nport * 2)); +} + +static void ub960_init_rx_port_ub9702_fpd4_aeq(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + bool first_time_power_up = true; + + if (first_time_power_up) { + u8 v; + + /* AEQ init */ + ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2c, &v); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, v); + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, v + 1); + + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x00); + } + + /* enable serdes_eq_ctl2 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x9e, 0x00); + + /* enable serdes_eq_ctl1 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x90, 0x40); + + /* enable serdes_eq_en */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2e, 0x40); + + /* disable serdes_eq_override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0xf0, 0x00); + + /* disable serdes_gain_override */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x71, 0x00); +} + +static void ub960_init_rx_port_ub9702_fpd4(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + u8 bc_freq_val; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + bc_freq_val = 0; + break; + + case RXPORT_MODE_RAW12_HF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_RAW12_LF: + bc_freq_val = 0; + break; + + case RXPORT_MODE_CSI2_SYNC: + bc_freq_val = 6; + break; + + case RXPORT_MODE_CSI2_NONSYNC: + bc_freq_val = 2; + break; + + default: + return; + } + + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, + bc_freq_val); + + /* FPD4 Sync Mode */ + ub960_rxport_write(priv, nport, UB960_RR_CHANNEL_MODE, 0); + + /* add serdes_eq_offset of 4 */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x2b, 0x04); + + /* FPD4 serdes_start_eq in 0x27: assign default */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x27, 0x0); + /* FPD4 serdes_end_eq in 0x28: assign default */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x28, 0x23); + + /* set serdes_driver_mode into FPD IV mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x04, 0x00); + /* set FPD PBC drv into FPD IV mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x1b, 0x00); + + /* set serdes_system_init to 0x2f */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x21, 0x2f); + /* set serdes_system_rst in reset mode */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0xc1); + + /* RX port to 7.55G mode */ + ub960_update_bits(priv, UB960_SR_FPD_RATE_CFG, 0x3 << (nport * 2), + 0 << (nport * 2)); + + ub960_init_rx_port_ub9702_fpd4_aeq(priv, rxport); +} + +static void ub960_init_rx_port_ub9702(struct ub960_data *priv, + struct ub960_rxport *rxport) +{ + unsigned int nport = rxport->nport; + + if (rxport->cdr_mode == RXPORT_CDR_FPD3) + ub960_init_rx_port_ub9702_fpd3(priv, rxport); + else /* RXPORT_CDR_FPD4 */ + ub960_init_rx_port_ub9702_fpd4(priv, rxport); + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + /* + * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits + * 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, + 0x3 << 6, 0x2 << 6); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + return; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + + break; + } + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, + rxport->lv_fv_pol); + + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + + /* Enable I2C communication to the serializer via the alias addr */ + ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, + rxport->ser.alias << 1); + + /* Enable RX port */ + ub960_update_bits(priv, UB960_SR_RX_PORT_CTL, BIT(nport), BIT(nport)); + + if (rxport->cdr_mode == RXPORT_CDR_FPD4) { + /* unreset 960 AEQ */ + ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport), 0x25, 0x41); + } +} + +static int ub960_init_rx_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + if (priv->hw_data->is_ub9702) + ub960_init_rx_port_ub9702(priv, rxport); + else + ub960_init_rx_port_ub960(priv, rxport); + } + + return 0; +} + +static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 rx_port_sts1; + u8 rx_port_sts2; + u8 csi_rx_sts; + u8 bcc_sts; + int ret = 0; + + /* Read interrupts (also clears most of them) */ + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, + &rx_port_sts2); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, + &csi_rx_sts); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, + &bcc_sts); + + if (ret) + return; + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PARITY_ERROR) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, + &v); + if (!ret) + dev_err(dev, "rx%u parity errors: %u\n", nport, v); + } + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR) + dev_err(dev, "rx%u BCC CRC error\n", nport); + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR) + dev_err(dev, "rx%u BCC SEQ error\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_UNSTABLE) + dev_err(dev, "rx%u line length unstable\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR) + dev_err(dev, "rx%u FPD3 encode error\n", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_BUFFER_ERROR) + dev_err(dev, "rx%u buffer error\n", nport); + + if (csi_rx_sts) + dev_err(dev, "rx%u CSI error: %#02x\n", nport, csi_rx_sts); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_ECC1_ERR) + dev_err(dev, "rx%u CSI ECC1 error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_ECC2_ERR) + dev_err(dev, "rx%u CSI ECC2 error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_CKSUM_ERR) + dev_err(dev, "rx%u CSI checksum error\n", nport); + + if (csi_rx_sts & UB960_RR_CSI_RX_STS_LENGTH_ERR) + dev_err(dev, "rx%u CSI length error\n", nport); + + if (bcc_sts) + dev_err(dev, "rx%u BCC error: %#02x\n", nport, bcc_sts); + + if (bcc_sts & UB960_RR_BCC_STATUS_RESP_ERR) + dev_err(dev, "rx%u BCC response error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SLAVE_TO) + dev_err(dev, "rx%u BCC slave timeout", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SLAVE_ERR) + dev_err(dev, "rx%u BCC slave error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_MASTER_TO) + dev_err(dev, "rx%u BCC master timeout", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_MASTER_ERR) + dev_err(dev, "rx%u BCC master error", nport); + + if (bcc_sts & UB960_RR_BCC_STATUS_SEQ_ERROR) + dev_err(dev, "rx%u BCC sequence error", nport); + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_LEN_CHG) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v); + if (!ret) + dev_dbg(dev, "rx%u line len changed: %u\n", nport, v); + } + + if (rx_port_sts2 & UB960_RR_RX_PORT_STS2_LINE_CNT_CHG) { + u16 v; + + ret = ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, + &v); + if (!ret) + dev_dbg(dev, "rx%u line count changed: %u\n", nport, v); + } + + if (rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS_CHG) { + dev_dbg(dev, "rx%u: %s, %s, %s, %s\n", nport, + (rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS) ? + "locked" : + "unlocked", + (rx_port_sts1 & UB960_RR_RX_PORT_STS1_PORT_PASS) ? + "passed" : + "not passed", + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_CABLE_FAULT) ? + "no clock" : + "clock ok", + (rx_port_sts2 & UB960_RR_RX_PORT_STS2_FREQ_STABLE) ? + "stable freq" : + "unstable freq"); + } +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +/* + * The current implementation only supports a simple VC mapping, where all VCs + * from a one RX port will be mapped to the same VC. Also, the hardware + * dictates that all streams from an RX port must go to a single TX port. + * + * This function decides the target VC numbers for each RX port with a simple + * algorithm, so that for each TX port, we get VC numbers starting from 0, + * and counting up. + * + * E.g. if all four RX ports are in use, of which the first two go to the + * first TX port and the secont two go to the second TX port, we would get + * the following VCs for the four RX ports: 0, 1, 0, 1. + * + * TODO: implement a more sophisticated VC mapping. As the driver cannot know + * what VCs the sinks expect (say, an FPGA with hardcoded VC routing), this + * probably needs to be somehow configurable. Device tree? + */ +static void ub960_get_vc_maps(struct ub960_data *priv, + struct v4l2_subdev_state *state, u8 *vc) +{ + u8 cur_vc[UB960_MAX_TX_NPORTS] = {}; + struct v4l2_subdev_route *route; + u8 handled_mask = 0; + + for_each_active_route(&state->routing, route) { + unsigned int rx, tx; + + rx = ub960_pad_to_port(priv, route->sink_pad); + if (BIT(rx) & handled_mask) + continue; + + tx = ub960_pad_to_port(priv, route->source_pad); + + vc[rx] = cur_vc[tx]++; + handled_mask |= BIT(rx); + } +} + +static int ub960_enable_tx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "enable TX port %u\n", nport); + + return ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, + UB960_TR_CSI_CTL_CSI_ENABLE); +} + +static void ub960_disable_tx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "disable TX port %u\n", nport); + + ub960_txport_update_bits(priv, nport, UB960_TR_CSI_CTL, + UB960_TR_CSI_CTL_CSI_ENABLE, 0); +} + +static int ub960_enable_rx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "enable RX port %u\n", nport); + + /* Enable forwarding */ + return ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), 0); +} + +static void ub960_disable_rx_port(struct ub960_data *priv, unsigned int nport) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "disable RX port %u\n", nport); + + /* Disable forwarding */ + ub960_update_bits(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(nport), + UB960_SR_FWD_CTL1_PORT_DIS(nport)); +} + +/* + * The driver only supports using a single VC for each source. This function + * checks that each source only provides streams using a single VC. + */ +static int ub960_validate_stream_vcs(struct ub960_data *priv) +{ + unsigned int nport; + unsigned int i; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + struct v4l2_mbus_frame_desc desc; + int ret; + u8 vc; + + if (!rxport) + continue; + + ret = v4l2_subdev_call(rxport->source.sd, pad, get_frame_desc, + rxport->source.pad, &desc); + if (ret) + return ret; + + if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) + continue; + + if (desc.num_entries == 0) + continue; + + vc = desc.entry[0].bus.csi2.vc; + + for (i = 1; i < desc.num_entries; i++) { + if (vc == desc.entry[i].bus.csi2.vc) + continue; + + dev_err(&priv->client->dev, + "rx%u: source with multiple virtual-channels is not supported\n", + nport); + return -ENODEV; + } + } + + return 0; +} + +static int ub960_configure_ports_for_streaming(struct ub960_data *priv, + struct v4l2_subdev_state *state) +{ + u8 fwd_ctl; + struct { + u32 num_streams; + u8 pixel_dt; + u8 meta_dt; + u32 meta_lines; + u32 tx_port; + } rx_data[UB960_MAX_RX_NPORTS] = {}; + u8 vc_map[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int nport; + int ret; + + ret = ub960_validate_stream_vcs(priv); + if (ret) + return ret; + + ub960_get_vc_maps(priv, state, vc_map); + + for_each_active_route(&state->routing, route) { + struct ub960_rxport *rxport; + struct ub960_txport *txport; + struct v4l2_mbus_framefmt *fmt; + const struct ub960_format_info *ub960_fmt; + unsigned int nport; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + rxport = priv->rxports[nport]; + if (!rxport) + return -EINVAL; + + txport = priv->txports[ub960_pad_to_port(priv, route->source_pad)]; + if (!txport) + return -EINVAL; + + rx_data[nport].tx_port = ub960_pad_to_port(priv, route->source_pad); + + rx_data[nport].num_streams++; + + /* For the rest, we are only interested in parallel busses */ + if (rxport->rx_mode == RXPORT_MODE_CSI2_SYNC || + rxport->rx_mode == RXPORT_MODE_CSI2_NONSYNC) + continue; + + if (rx_data[nport].num_streams > 2) + return -EPIPE; + + fmt = v4l2_subdev_state_get_stream_format(state, + route->sink_pad, + route->sink_stream); + if (!fmt) + return -EPIPE; + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) + return -EPIPE; + + if (ub960_fmt->meta) { + if (fmt->height > 3) { + dev_err(&priv->client->dev, + "rx%u: unsupported metadata height %u\n", + nport, fmt->height); + return -EPIPE; + } + + rx_data[nport].meta_dt = ub960_fmt->datatype; + rx_data[nport].meta_lines = fmt->height; + } else { + rx_data[nport].pixel_dt = ub960_fmt->datatype; + } + } + + /* Configure RX ports */ + + /* + * Keep all port forwardings disabled by default. Forwarding will be + * enabled in ub960_enable_rx_port. + */ + fwd_ctl = GENMASK(7, 4); + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + u8 vc = vc_map[nport]; + + if (rx_data[nport].num_streams == 0) + continue; + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, + rx_data[nport].pixel_dt | (vc << UB960_RR_RAW10_ID_VC_SHIFT)); + + ub960_rxport_write(priv, rxport->nport, + UB960_RR_RAW_EMBED_DTYPE, + (rx_data[nport].meta_lines << UB960_RR_RAW_EMBED_DTYPE_LINES_SHIFT) | + rx_data[nport].meta_dt); + + break; + + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + /* Not implemented */ + break; + + case RXPORT_MODE_CSI2_SYNC: + case RXPORT_MODE_CSI2_NONSYNC: + if (!priv->hw_data->is_ub9702) { + /* Map all VCs from this port to the same VC */ + ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP, + (vc << UB960_RR_CSI_VC_MAP_SHIFT(3)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(2)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(1)) | + (vc << UB960_RR_CSI_VC_MAP_SHIFT(0))); + } else { + unsigned int i; + + /* Map all VCs from this port to VC(nport) */ + for (i = 0; i < 8; i++) + ub960_rxport_write(priv, nport, + UB960_RR_VC_ID_MAP(i), + nport); + } + + break; + } + + if (rx_data[nport].tx_port == 1) + fwd_ctl |= BIT(nport); /* forward to TX1 */ + else + fwd_ctl &= ~BIT(nport); /* forward to TX0 */ + } + + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl); + + return 0; +} + +static void ub960_update_streaming_status(struct ub960_data *priv) +{ + unsigned int i; + + for (i = 0; i < UB960_MAX_NPORTS; i++) { + if (priv->stream_enable_mask[i]) + break; + } + + priv->streaming = i < UB960_MAX_NPORTS; +} + +static int ub960_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 source_pad, + u64 source_streams_mask) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int failed_port; + unsigned int nport; + int ret; + + if (!priv->streaming) { + dev_dbg(dev, "Prepare for streaming\n"); + ret = ub960_configure_ports_for_streaming(priv, state); + if (ret) + return ret; + } + + /* Enable TX port if not yet enabled */ + if (!priv->stream_enable_mask[source_pad]) { + ret = ub960_enable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + if (ret) + return ret; + } + + priv->stream_enable_mask[source_pad] |= source_streams_mask; + + /* Collect sink streams per pad which we need to enable */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != source_pad) + continue; + + if (!(source_streams_mask & BIT_ULL(route->source_stream))) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + sink_streams[nport] |= BIT_ULL(route->sink_stream); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + if (!sink_streams[nport]) + continue; + + /* Enable the RX port if not yet enabled */ + if (!priv->stream_enable_mask[nport]) { + ret = ub960_enable_rx_port(priv, nport); + if (ret) { + failed_port = nport; + goto err; + } + } + + priv->stream_enable_mask[nport] |= sink_streams[nport]; + + dev_dbg(dev, "enable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_enable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) { + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + + failed_port = nport; + goto err; + } + } + + priv->streaming = true; + + return 0; + +err: + for (nport = 0; nport < failed_port; nport++) { + if (!sink_streams[nport]) + continue; + + dev_dbg(dev, "disable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_disable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) + dev_err(dev, "Failed to disable streams: %d\n", ret); + + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + /* Disable RX port if no active streams */ + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + } + + priv->stream_enable_mask[source_pad] &= ~source_streams_mask; + + if (!priv->stream_enable_mask[source_pad]) + ub960_disable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + + ub960_update_streaming_status(priv); + + return ret; +} + +static int ub960_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 source_streams_mask) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams[UB960_MAX_RX_NPORTS] = {}; + struct v4l2_subdev_route *route; + unsigned int nport; + int ret; + + /* Collect sink streams per pad which we need to disable */ + for_each_active_route(&state->routing, route) { + if (route->source_pad != source_pad) + continue; + + if (!(source_streams_mask & BIT_ULL(route->source_stream))) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + sink_streams[nport] |= BIT_ULL(route->sink_stream); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + if (!sink_streams[nport]) + continue; + + dev_dbg(dev, "disable RX port %u streams %#llx\n", nport, + sink_streams[nport]); + + ret = v4l2_subdev_disable_streams( + priv->rxports[nport]->source.sd, + priv->rxports[nport]->source.pad, + sink_streams[nport]); + if (ret) + dev_err(dev, "Failed to disable streams: %d\n", ret); + + priv->stream_enable_mask[nport] &= ~sink_streams[nport]; + + /* Disable RX port if no active streams */ + if (!priv->stream_enable_mask[nport]) + ub960_disable_rx_port(priv, nport); + } + + /* Disable TX port if no active streams */ + + priv->stream_enable_mask[source_pad] &= ~source_streams_mask; + + if (!priv->stream_enable_mask[source_pad]) + ub960_disable_tx_port(priv, + ub960_pad_to_port(priv, source_pad)); + + ub960_update_streaming_status(priv); + + return 0; +} + +static int _ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -E2BIG; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + return _ub960_set_routing(sd, state, routing); +} + +static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct v4l2_subdev_route *route; + struct v4l2_subdev_state *state; + int ret = 0; + struct device *dev = &priv->client->dev; + u8 vc_map[UB960_MAX_RX_NPORTS] = {}; + + if (!ub960_pad_is_source(priv, pad)) + return -EINVAL; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + state = v4l2_subdev_lock_and_get_active_state(&priv->sd); + + ub960_get_vc_maps(priv, state, vc_map); + + for_each_active_route(&state->routing, route) { + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + struct v4l2_mbus_frame_desc source_fd; + unsigned int nport; + unsigned int i; + + if (route->source_pad != pad) + continue; + + nport = ub960_pad_to_port(priv, route->sink_pad); + + ret = v4l2_subdev_call(priv->rxports[nport]->source.sd, pad, + get_frame_desc, + priv->rxports[nport]->source.pad, + &source_fd); + if (ret) { + dev_err(dev, + "Failed to get source frame desc for pad %u\n", + route->sink_pad); + goto out_unlock; + } + + for (i = 0; i < source_fd.num_entries; i++) { + if (source_fd.entry[i].stream == route->sink_stream) { + source_entry = &source_fd.entry[i]; + break; + } + } + + if (!source_entry) { + dev_err(dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out_unlock; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + fd->entry[fd->num_entries].flags = source_entry->flags; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; + + fd->entry[fd->num_entries].bus.csi2.vc = vc_map[nport]; + + if (source_fd.type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + } else { + const struct ub960_format_info *ub960_fmt; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_stream_format(state, pad, + route->source_stream); + + if (!fmt) { + ret = -EINVAL; + goto out_unlock; + } + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) { + dev_err(dev, "Unable to find format\n"); + ret = -EINVAL; + goto out_unlock; + } + + fd->entry[fd->num_entries].bus.csi2.dt = + ub960_fmt->datatype; + } + + fd->num_entries++; + } + +out_unlock: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub960_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (ub960_pad_is_source(priv, format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* + * Default to the first format if the requested media bus code isn't + * supported. + */ + if (!ub960_find_format(format->format.code)) + format->format.code = ub960_formats[0].code; + + fmt = v4l2_subdev_state_get_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int ub960_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + struct v4l2_subdev_route routes[] = { + { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = priv->hw_data->num_rxports, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub960_set_routing(sd, state, &routing); +} + +static const struct v4l2_subdev_pad_ops ub960_pad_ops = { + .enable_streams = ub960_enable_streams, + .disable_streams = ub960_disable_streams, + + .set_routing = ub960_set_routing, + .get_frame_desc = ub960_get_frame_desc, + + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub960_set_fmt, + + .init_cfg = ub960_init_cfg, +}; + +static int ub960_log_status(struct v4l2_subdev *sd) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct device *dev = &priv->client->dev; + struct v4l2_subdev_state *state; + unsigned int nport; + unsigned int i; + u16 v16 = 0; + u8 v = 0; + u8 id[UB960_SR_FPD3_RX_ID_LEN]; + + state = v4l2_subdev_lock_and_get_active_state(sd); + + for (i = 0; i < sizeof(id); i++) + ub960_read(priv, UB960_SR_FPD3_RX_ID(i), &id[i]); + + dev_info(dev, "ID '%.*s'\n", (int)sizeof(id), id); + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + dev_info(dev, "TX %u\n", nport); + + if (!txport) { + dev_info(dev, "\tNot initialized\n"); + continue; + } + + ub960_txport_read(priv, nport, UB960_TR_CSI_STS, &v); + dev_info(dev, "\tsync %u, pass %u\n", v & (u8)BIT(1), + v & (u8)BIT(0)); + + ub960_read16(priv, UB960_SR_CSI_FRAME_COUNT_HI(nport), &v16); + dev_info(dev, "\tframe counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_FRAME_ERR_COUNT_HI(nport), &v16); + dev_info(dev, "\tframe error counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_LINE_COUNT_HI(nport), &v16); + dev_info(dev, "\tline counter %u\n", v16); + + ub960_read16(priv, UB960_SR_CSI_LINE_ERR_COUNT_HI(nport), &v16); + dev_info(dev, "\tline error counter %u\n", v16); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + u8 eq_level; + s8 strobe_pos; + unsigned int i; + + dev_info(dev, "RX %u\n", nport); + + if (!rxport) { + dev_info(dev, "\tNot initialized\n"); + continue; + } + + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &v); + + if (v & UB960_RR_RX_PORT_STS1_LOCK_STS) + dev_info(dev, "\tLocked\n"); + else + dev_info(dev, "\tNot locked\n"); + + dev_info(dev, "\trx_port_sts1 %#02x\n", v); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &v); + dev_info(dev, "\trx_port_sts2 %#02x\n", v); + + ub960_rxport_read16(priv, nport, UB960_RR_RX_FREQ_HIGH, &v16); + dev_info(dev, "\tlink freq %llu Hz\n", (v16 * 1000000ULL) >> 8); + + ub960_rxport_read16(priv, nport, UB960_RR_RX_PAR_ERR_HI, &v16); + dev_info(dev, "\tparity errors %u\n", v16); + + ub960_rxport_read16(priv, nport, UB960_RR_LINE_COUNT_HI, &v16); + dev_info(dev, "\tlines per frame %u\n", v16); + + ub960_rxport_read16(priv, nport, UB960_RR_LINE_LEN_1, &v16); + dev_info(dev, "\tbytes per line %u\n", v16); + + ub960_rxport_read(priv, nport, UB960_RR_CSI_ERR_COUNTER, &v); + dev_info(dev, "\tcsi_err_counter %u\n", v); + + /* Strobe */ + + ub960_read(priv, UB960_XR_AEQ_CTL1, &v); + + dev_info(dev, "\t%s strobe\n", + (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" : + "Manual"); + + if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) { + ub960_read(priv, UB960_XR_SFILTER_CFG, &v); + + dev_info(dev, "\tStrobe range [%d, %d]\n", + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7, + ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7); + } + + ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos); + + dev_info(dev, "\tStrobe pos %d\n", strobe_pos); + + /* EQ */ + + ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v); + + dev_info(dev, "\t%s EQ\n", + (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" : + "Adaptive"); + + if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) { + ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v); + + dev_info(dev, "\tEQ range [%u, %u]\n", + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf, + (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf); + } + + if (ub960_rxport_get_eq_level(priv, nport, &eq_level) == 0) + dev_info(dev, "\tEQ level %u\n", eq_level); + + /* GPIOs */ + for (i = 0; i < UB960_NUM_BC_GPIOS; i++) { + u8 ctl_reg; + u8 ctl_shift; + + ctl_reg = UB960_RR_BC_GPIO_CTL(i / 2); + ctl_shift = (i % 2) * 4; + + ub960_rxport_read(priv, nport, ctl_reg, &v); + + dev_info(dev, "\tGPIO%u: mode %u\n", i, + (v >> ctl_shift) & 0xf); + } + } + + v4l2_subdev_unlock_state(state); + + return 0; +} + +static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { + .log_status = ub960_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ub960_subdev_ops = { + .core = &ub960_subdev_core_ops, + .pad = &ub960_pad_ops, +}; + +static const struct media_entity_operations ub960_entity_ops = { + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .link_validate = v4l2_subdev_link_validate, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, +}; + +/* ----------------------------------------------------------------------------- + * Core + */ + +static irqreturn_t ub960_handle_events(int irq, void *arg) +{ + struct ub960_data *priv = arg; + unsigned int i; + u8 int_sts; + u8 fwd_sts; + int ret; + + ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts); + if (ret || !int_sts) + return IRQ_NONE; + + dev_dbg(&priv->client->dev, "INTERRUPT_STS %x\n", int_sts); + + ret = ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts); + if (ret) + return IRQ_NONE; + + dev_dbg(&priv->client->dev, "FWD_STS %#02x\n", fwd_sts); + + for (i = 0; i < priv->hw_data->num_txports; i++) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) + ub960_csi_handle_events(priv, i); + } + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + if (!priv->rxports[i]) + continue; + + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) + ub960_rxport_handle_events(priv, i); + } + + return IRQ_HANDLED; +} + +static void ub960_handler_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct ub960_data *priv = + container_of(dwork, struct ub960_data, poll_work); + + ub960_handle_events(0, priv); + + schedule_delayed_work(&priv->poll_work, + msecs_to_jiffies(UB960_POLL_TIME_MS)); +} + +static void ub960_txport_free_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + if (!txport) + continue; + + kfree(txport); + priv->txports[nport] = NULL; + } +} + +static void ub960_rxport_free_ports(struct ub960_data *priv) +{ + unsigned int nport; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + fwnode_handle_put(rxport->source.ep_fwnode); + fwnode_handle_put(rxport->ser.fwnode); + + kfree(rxport); + priv->rxports[nport] = NULL; + } +} + +static int +ub960_parse_dt_rxport_link_properties(struct ub960_data *priv, + struct fwnode_handle *link_fwnode, + struct ub960_rxport *rxport) +{ + struct device *dev = &priv->client->dev; + unsigned int nport = rxport->nport; + u32 rx_mode; + u32 cdr_mode; + s32 strobe_pos; + u32 eq_level; + u32 ser_i2c_alias; + int ret; + + cdr_mode = RXPORT_CDR_FPD3; + + ret = fwnode_property_read_u32(link_fwnode, "ti,cdr-mode", &cdr_mode); + if (ret < 0 && ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,cdr-mode", ret); + return ret; + } + + if (cdr_mode > RXPORT_CDR_LAST) { + dev_err(dev, "rx%u: bad 'ti,cdr-mode' %u\n", nport, cdr_mode); + return -EINVAL; + } + + if (!priv->hw_data->is_fpdlink4 && cdr_mode == RXPORT_CDR_FPD4) { + dev_err(dev, "rx%u: FPD-Link 4 CDR not supported\n", nport); + return -EINVAL; + } + + rxport->cdr_mode = cdr_mode; + + ret = fwnode_property_read_u32(link_fwnode, "ti,rx-mode", &rx_mode); + if (ret < 0) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,rx-mode", ret); + return ret; + } + + if (rx_mode > RXPORT_MODE_LAST) { + dev_err(dev, "rx%u: bad 'ti,rx-mode' %u\n", nport, rx_mode); + return -EINVAL; + } + + switch (rx_mode) { + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + dev_err(dev, "rx%u: unsupported 'ti,rx-mode' %u\n", nport, + rx_mode); + return -EINVAL; + default: + break; + } + + rxport->rx_mode = rx_mode; + + /* EQ & Strobe related */ + + /* Defaults */ + rxport->eq.manual_eq = false; + rxport->eq.aeq.eq_level_min = UB960_MIN_EQ_LEVEL; + rxport->eq.aeq.eq_level_max = UB960_MAX_EQ_LEVEL; + + ret = fwnode_property_read_u32(link_fwnode, "ti,strobe-pos", + &strobe_pos); + if (ret) { + if (ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,strobe-pos", ret); + return ret; + } + } else { + if (strobe_pos < UB960_MIN_MANUAL_STROBE_POS || + strobe_pos > UB960_MAX_MANUAL_STROBE_POS) { + dev_err(dev, "rx%u: illegal 'strobe-pos' value: %d\n", + nport, strobe_pos); + return -EINVAL; + } + + /* NOTE: ignored unless global manual strobe pos is also set */ + rxport->eq.strobe_pos = strobe_pos; + if (!priv->strobe.manual) + dev_warn(dev, + "rx%u: 'ti,strobe-pos' ignored as 'ti,manual-strobe' not set\n", + nport); + } + + ret = fwnode_property_read_u32(link_fwnode, "ti,eq-level", &eq_level); + if (ret) { + if (ret != -EINVAL) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "ti,eq-level", ret); + return ret; + } + } else { + if (eq_level > UB960_MAX_EQ_LEVEL) { + dev_err(dev, "rx%u: illegal 'ti,eq-level' value: %d\n", + nport, eq_level); + return -EINVAL; + } + + rxport->eq.manual_eq = true; + rxport->eq.manual.eq_level = eq_level; + } + + ret = fwnode_property_read_u32(link_fwnode, "i2c-alias", + &ser_i2c_alias); + if (ret) { + dev_err(dev, "rx%u: failed to read '%s': %d\n", nport, + "i2c-alias", ret); + return ret; + } + rxport->ser.alias = ser_i2c_alias; + + rxport->ser.fwnode = fwnode_get_named_child_node(link_fwnode, "serializer"); + if (!rxport->ser.fwnode) { + dev_err(dev, "rx%u: missing 'serializer' node\n", nport); + return -EINVAL; + } + + return 0; +} + +static int ub960_parse_dt_rxport_ep_properties(struct ub960_data *priv, + struct fwnode_handle *ep_fwnode, + struct ub960_rxport *rxport) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = {}; + unsigned int nport = rxport->nport; + bool hsync_hi; + bool vsync_hi; + int ret; + + rxport->source.ep_fwnode = fwnode_graph_get_remote_endpoint(ep_fwnode); + if (!rxport->source.ep_fwnode) { + dev_err(dev, "rx%u: no remote endpoint\n", nport); + return -ENODEV; + } + + /* We currently have properties only for RAW modes */ + + switch (rxport->rx_mode) { + case RXPORT_MODE_RAW10: + case RXPORT_MODE_RAW12_HF: + case RXPORT_MODE_RAW12_LF: + break; + default: + return 0; + } + + vep.bus_type = V4L2_MBUS_PARALLEL; + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + if (ret) { + dev_err(dev, "rx%u: failed to parse endpoint data\n", nport); + goto err_put_source_ep_fwnode; + } + + hsync_hi = !!(vep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH); + vsync_hi = !!(vep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH); + + /* LineValid and FrameValid are inverse to the h/vsync active */ + rxport->lv_fv_pol = (hsync_hi ? UB960_RR_PORT_CONFIG2_LV_POL_LOW : 0) | + (vsync_hi ? UB960_RR_PORT_CONFIG2_FV_POL_LOW : 0); + + return 0; + +err_put_source_ep_fwnode: + fwnode_handle_put(rxport->source.ep_fwnode); + return ret; +} + +static int ub960_parse_dt_rxport(struct ub960_data *priv, unsigned int nport, + struct fwnode_handle *link_fwnode, + struct fwnode_handle *ep_fwnode) +{ + static const char *vpoc_names[UB960_MAX_RX_NPORTS] = { + "vpoc0", "vpoc1", "vpoc2", "vpoc3" + }; + struct device *dev = &priv->client->dev; + struct ub960_rxport *rxport; + int ret; + + rxport = kzalloc(sizeof(*rxport), GFP_KERNEL); + if (!rxport) + return -ENOMEM; + + priv->rxports[nport] = rxport; + + rxport->nport = nport; + rxport->priv = priv; + + ret = ub960_parse_dt_rxport_link_properties(priv, link_fwnode, rxport); + if (ret) + goto err_free_rxport; + + rxport->vpoc = devm_regulator_get_optional(dev, vpoc_names[nport]); + if (IS_ERR(rxport->vpoc)) { + ret = PTR_ERR(rxport->vpoc); + if (ret == -ENODEV) { + rxport->vpoc = NULL; + } else { + dev_err(dev, "rx%u: failed to get VPOC supply: %d\n", + nport, ret); + goto err_put_remote_fwnode; + } + } + + ret = ub960_parse_dt_rxport_ep_properties(priv, ep_fwnode, rxport); + if (ret) + goto err_put_remote_fwnode; + + return 0; + +err_put_remote_fwnode: + fwnode_handle_put(rxport->ser.fwnode); +err_free_rxport: + priv->rxports[nport] = NULL; + kfree(rxport); + return ret; +} + +static struct fwnode_handle * +ub960_fwnode_get_link_by_regs(struct fwnode_handle *links_fwnode, + unsigned int nport) +{ + struct fwnode_handle *link_fwnode; + int ret; + + fwnode_for_each_child_node(links_fwnode, link_fwnode) { + u32 link_num; + + if (!str_has_prefix(fwnode_get_name(link_fwnode), "link@")) + continue; + + ret = fwnode_property_read_u32(link_fwnode, "reg", &link_num); + if (ret) { + fwnode_handle_put(link_fwnode); + return NULL; + } + + if (nport == link_num) + return link_fwnode; + } + + return NULL; +} + +static int ub960_parse_dt_rxports(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + struct fwnode_handle *links_fwnode; + unsigned int nport; + int ret; + + links_fwnode = fwnode_get_named_child_node(dev_fwnode(dev), "links"); + if (!links_fwnode) { + dev_err(dev, "'links' node missing\n"); + return -ENODEV; + } + + /* Defaults, recommended by TI */ + priv->strobe.min = 2; + priv->strobe.max = 3; + + priv->strobe.manual = fwnode_property_read_bool(links_fwnode, "ti,manual-strobe"); + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct fwnode_handle *link_fwnode; + struct fwnode_handle *ep_fwnode; + + link_fwnode = ub960_fwnode_get_link_by_regs(links_fwnode, nport); + if (!link_fwnode) + continue; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + nport, 0, 0); + if (!ep_fwnode) { + fwnode_handle_put(link_fwnode); + continue; + } + + ret = ub960_parse_dt_rxport(priv, nport, link_fwnode, + ep_fwnode); + + fwnode_handle_put(link_fwnode); + fwnode_handle_put(ep_fwnode); + + if (ret) { + dev_err(dev, "rx%u: failed to parse RX port\n", nport); + goto err_put_links; + } + } + + fwnode_handle_put(links_fwnode); + + return 0; + +err_put_links: + fwnode_handle_put(links_fwnode); + + return ret; +} + +static int ub960_parse_dt_txports(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + u32 nport; + int ret; + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + unsigned int port = nport + priv->hw_data->num_rxports; + struct fwnode_handle *ep_fwnode; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + port, 0, 0); + if (!ep_fwnode) + continue; + + ret = ub960_parse_dt_txport(priv, ep_fwnode, nport); + + fwnode_handle_put(ep_fwnode); + + if (ret) + break; + } + + return 0; +} + +static int ub960_parse_dt(struct ub960_data *priv) +{ + int ret; + + ret = ub960_parse_dt_rxports(priv); + if (ret) + return ret; + + ret = ub960_parse_dt_txports(priv); + if (ret) + goto err_free_rxports; + + return 0; + +err_free_rxports: + ub960_rxport_free_ports(priv); + + return ret; +} + +static int ub960_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct ub960_data *priv = sd_to_ub960(notifier->sd); + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + struct device *dev = &priv->client->dev; + u8 nport = rxport->nport; + unsigned int i; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + rxport->source.ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + rxport->source.sd = subdev; + rxport->source.pad = ret; + + ret = media_create_pad_link(&rxport->source.sd->entity, + rxport->source.pad, &priv->sd.entity, nport, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:%u\n", + rxport->source.sd->name, rxport->source.pad, + priv->sd.name, nport); + return ret; + } + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + if (priv->rxports[i] && !priv->rxports[i]->source.sd) { + dev_dbg(dev, "Waiting for more subdevs to be bound\n"); + return 0; + } + } + + return 0; +} + +static void ub960_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + + rxport->source.sd = NULL; +} + +static const struct v4l2_async_notifier_operations ub960_notify_ops = { + .bound = ub960_notify_bound, + .unbind = ub960_notify_unbind, +}; + +static int ub960_v4l2_notifier_register(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + struct ub960_rxport *rxport = priv->rxports[i]; + struct ub960_asd *asd; + + if (!rxport) + continue; + + asd = v4l2_async_nf_add_fwnode(&priv->notifier, + rxport->source.ep_fwnode, + struct ub960_asd); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev for source %u: %pe", + i, asd); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + asd->rxport = rxport; + } + + priv->notifier.ops = &ub960_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub960_v4l2_notifier_unregister(struct ub960_data *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +} + +static int ub960_create_subdev(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(priv->tx_link_freq) - 1, 0, + priv->tx_link_freq); + + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub960_entity_ops; + + for (i = 0; i < priv->hw_data->num_rxports + priv->hw_data->num_txports; i++) { + priv->pads[i].flags = ub960_pad_is_sink(priv, i) ? + MEDIA_PAD_FL_SINK : + MEDIA_PAD_FL_SOURCE; + } + + ret = media_entity_pads_init(&priv->sd.entity, + priv->hw_data->num_rxports + + priv->hw_data->num_txports, + priv->pads); + if (ret) + goto err_free_ctrl; + + priv->sd.state_lock = priv->sd.ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub960_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub960_v4l2_notifier_unregister(priv); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +} + +static void ub960_destroy_subdev(struct ub960_data *priv) +{ + ub960_v4l2_notifier_unregister(priv); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static const struct regmap_config ub960_regmap_config = { + .name = "ds90ub960", + + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + + /* + * We do locking in the driver to cover the TX/RX port selection and the + * indirect register access. + */ + .disable_locking = true, +}; + +static void ub960_reset(struct ub960_data *priv, bool reset_regs) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + u8 bit; + + bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 : + UB960_SR_RESET_DIGITAL_RESET0; + + ub960_write(priv, UB960_SR_RESET, bit); + + mutex_lock(&priv->reg_lock); + + ret = regmap_read_poll_timeout(priv->regmap, UB960_SR_RESET, v, + (v & bit) == 0, 2000, 100000); + + mutex_unlock(&priv->reg_lock); + + if (ret) + dev_err(dev, "reset failed: %d\n", ret); +} + +static int ub960_get_hw_resources(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + + priv->regmap = devm_regmap_init_i2c(priv->client, &ub960_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->vddio = devm_regulator_get(dev, "vddio"); + if (IS_ERR(priv->vddio)) + return dev_err_probe(dev, PTR_ERR(priv->vddio), + "cannot get VDDIO regulator\n"); + + /* get power-down pin from DT */ + priv->pd_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->pd_gpio), + "Cannot get powerdown GPIO\n"); + + priv->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(priv->refclk)) + return dev_err_probe(dev, PTR_ERR(priv->refclk), + "Cannot get REFCLK\n"); + + return 0; +} + +static int ub960_enable_core_hw(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + u8 rev_mask; + int ret; + u8 dev_sts; + u8 refclk_freq; + + ret = regulator_enable(priv->vddio); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable VDDIO regulator\n"); + + ret = clk_prepare_enable(priv->refclk); + if (ret) { + dev_err_probe(dev, ret, "Failed to enable refclk\n"); + goto err_disable_vddio; + } + + if (priv->pd_gpio) { + gpiod_set_value_cansleep(priv->pd_gpio, 1); + /* wait min 2 ms for reset to complete */ + fsleep(2000); + gpiod_set_value_cansleep(priv->pd_gpio, 0); + /* wait min 2 ms for power up to finish */ + fsleep(2000); + } + + ub960_reset(priv, true); + + /* Runtime check register accessibility */ + ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask); + if (ret) { + dev_err_probe(dev, ret, "Cannot read first register, abort\n"); + goto err_pd_gpio; + } + + dev_dbg(dev, "Found %s (rev/mask %#04x)\n", priv->hw_data->model, + rev_mask); + + ret = ub960_read(priv, UB960_SR_DEVICE_STS, &dev_sts); + if (ret) + goto err_pd_gpio; + + ret = ub960_read(priv, UB960_XR_REFCLK_FREQ, &refclk_freq); + if (ret) + goto err_pd_gpio; + + dev_dbg(dev, "refclk valid %u freq %u MHz (clk fw freq %lu MHz)\n", + !!(dev_sts & BIT(4)), refclk_freq, + clk_get_rate(priv->refclk) / 1000000); + + /* Disable all RX ports by default */ + ret = ub960_write(priv, UB960_SR_RX_PORT_CTL, 0); + if (ret) + goto err_pd_gpio; + + /* release GPIO lock */ + if (priv->hw_data->is_ub9702) { + ret = ub960_update_bits(priv, UB960_SR_RESET, + UB960_SR_RESET_GPIO_LOCK_RELEASE, + UB960_SR_RESET_GPIO_LOCK_RELEASE); + if (ret) + goto err_pd_gpio; + } + + return 0; + +err_pd_gpio: + gpiod_set_value_cansleep(priv->pd_gpio, 1); + clk_disable_unprepare(priv->refclk); +err_disable_vddio: + regulator_disable(priv->vddio); + + return ret; +} + +static void ub960_disable_core_hw(struct ub960_data *priv) +{ + gpiod_set_value_cansleep(priv->pd_gpio, 1); + clk_disable_unprepare(priv->refclk); + regulator_disable(priv->vddio); +} + +static int ub960_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub960_data *priv; + unsigned int port_lock_mask; + unsigned int port_mask; + unsigned int nport; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->hw_data = device_get_match_data(dev); + + mutex_init(&priv->reg_lock); + + INIT_DELAYED_WORK(&priv->poll_work, ub960_handler_work); + + /* + * Initialize these to invalid values so that the first reg writes will + * configure the target. + */ + priv->reg_current.indirect_target = 0xff; + priv->reg_current.rxport = 0xff; + priv->reg_current.txport = 0xff; + + ret = ub960_get_hw_resources(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub960_enable_core_hw(priv); + if (ret) + goto err_mutex_destroy; + + ret = ub960_parse_dt(priv); + if (ret) + goto err_disable_core_hw; + + ret = ub960_init_tx_ports(priv); + if (ret) + goto err_free_ports; + + ret = ub960_rxport_enable_vpocs(priv); + if (ret) + goto err_free_ports; + + ret = ub960_init_rx_ports(priv); + if (ret) + goto err_disable_vpocs; + + ub960_reset(priv, false); + + port_mask = 0; + + for (nport = 0; nport < priv->hw_data->num_rxports; nport++) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport) + continue; + + port_mask |= BIT(nport); + } + + ret = ub960_rxport_wait_locks(priv, port_mask, &port_lock_mask); + if (ret) + goto err_disable_vpocs; + + if (port_mask != port_lock_mask) { + ret = -EIO; + dev_err_probe(dev, ret, "Failed to lock all RX ports\n"); + goto err_disable_vpocs; + } + + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + ub960_clear_rx_errors(priv); + + ret = ub960_init_atr(priv); + if (ret) + goto err_disable_vpocs; + + ret = ub960_rxport_add_serializers(priv); + if (ret) + goto err_uninit_atr; + + ret = ub960_create_subdev(priv); + if (ret) + goto err_free_sers; + + if (client->irq) + dev_warn(dev, "irq support not implemented, using polling\n"); + + schedule_delayed_work(&priv->poll_work, + msecs_to_jiffies(UB960_POLL_TIME_MS)); + + return 0; + +err_free_sers: + ub960_rxport_remove_serializers(priv); +err_uninit_atr: + ub960_uninit_atr(priv); +err_disable_vpocs: + ub960_rxport_disable_vpocs(priv); +err_free_ports: + ub960_rxport_free_ports(priv); + ub960_txport_free_ports(priv); +err_disable_core_hw: + ub960_disable_core_hw(priv); +err_mutex_destroy: + mutex_destroy(&priv->reg_lock); + return ret; +} + +static void ub960_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub960_data *priv = sd_to_ub960(sd); + + cancel_delayed_work_sync(&priv->poll_work); + + ub960_destroy_subdev(priv); + ub960_rxport_remove_serializers(priv); + ub960_uninit_atr(priv); + ub960_rxport_disable_vpocs(priv); + ub960_rxport_free_ports(priv); + ub960_txport_free_ports(priv); + ub960_disable_core_hw(priv); + mutex_destroy(&priv->reg_lock); +} + +static const struct ub960_hw_data ds90ub960_hw = { + .model = "ub960", + .num_rxports = 4, + .num_txports = 2, +}; + +static const struct ub960_hw_data ds90ub9702_hw = { + .model = "ub9702", + .num_rxports = 4, + .num_txports = 2, + .is_ub9702 = true, + .is_fpdlink4 = true, +}; + +static const struct i2c_device_id ub960_id[] = { + { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw }, + { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ub960_id); + +static const struct of_device_id ub960_dt_ids[] = { + { .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw }, + { .compatible = "ti,ds90ub9702-q1", .data = &ds90ub9702_hw }, + {} +}; +MODULE_DEVICE_TABLE(of, ub960_dt_ids); + +static struct i2c_driver ds90ub960_driver = { + .probe = ub960_probe, + .remove = ub960_remove, + .id_table = ub960_id, + .driver = { + .name = "ds90ub960", + .of_match_table = ub960_dt_ids, + }, +}; +module_i2c_driver(ds90ub960_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments FPD-Link III/IV Deserializers Driver"); +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); +MODULE_IMPORT_NS(I2C_ATR); diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c new file mode 100644 index 000000000000..c626ed845928 --- /dev/null +++ b/drivers/media/i2c/dw9719.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2012 Intel Corporation + +/* + * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo: + * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#define DW9719_MAX_FOCUS_POS 1023 +#define DW9719_CTRL_STEPS 16 +#define DW9719_CTRL_DELAY_US 1000 + +#define DW9719_INFO CCI_REG8(0) +#define DW9719_ID 0xF1 + +#define DW9719_CONTROL CCI_REG8(2) +#define DW9719_ENABLE_RINGING 0x02 + +#define DW9719_VCM_CURRENT CCI_REG16(3) + +#define DW9719_MODE CCI_REG8(6) +#define DW9719_MODE_SAC_SHIFT 4 +#define DW9719_MODE_SAC3 4 + +#define DW9719_VCM_FREQ CCI_REG8(7) +#define DW9719_DEFAULT_VCM_FREQ 0x60 + +#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd) + +struct dw9719_device { + struct v4l2_subdev sd; + struct device *dev; + struct regmap *regmap; + struct regulator *regulator; + u32 sac_mode; + u32 vcm_freq; + + struct dw9719_v4l2_ctrls { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *focus; + } ctrls; +}; + +static int dw9719_detect(struct dw9719_device *dw9719) +{ + int ret; + u64 val; + + ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL); + if (ret < 0) + return ret; + + if (val != DW9719_ID) { + dev_err(dw9719->dev, "Failed to detect correct id\n"); + return -ENXIO; + } + + return 0; +} + +static int dw9719_power_down(struct dw9719_device *dw9719) +{ + return regulator_disable(dw9719->regulator); +} + +static int dw9719_power_up(struct dw9719_device *dw9719) +{ + int ret; + + ret = regulator_enable(dw9719->regulator); + if (ret) + return ret; + + /* Jiggle SCL pin to wake up device */ + cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret); + + /* Need 100us to transit from SHUTDOWN to STANDBY */ + fsleep(100); + + cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret); + cci_write(dw9719->regmap, DW9719_MODE, + dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret); + cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret); + + if (ret) + dw9719_power_down(dw9719); + + return ret; +} + +static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value) +{ + return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL); +} + +static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9719_device *dw9719 = container_of(ctrl->handler, + struct dw9719_device, + ctrls.handler); + int ret; + + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(dw9719->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_FOCUS_ABSOLUTE: + ret = dw9719_t_focus_abs(dw9719, ctrl->val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_put(dw9719->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops dw9719_ctrl_ops = { + .s_ctrl = dw9719_set_ctrl, +}; + +static int dw9719_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct dw9719_device *dw9719 = to_dw9719_device(sd); + int ret; + int val; + + for (val = dw9719->ctrls.focus->val; val >= 0; + val -= DW9719_CTRL_STEPS) { + ret = dw9719_t_focus_abs(dw9719, val); + if (ret) + return ret; + + usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10); + } + + return dw9719_power_down(dw9719); +} + +static int dw9719_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct dw9719_device *dw9719 = to_dw9719_device(sd); + int current_focus = dw9719->ctrls.focus->val; + int ret; + int val; + + ret = dw9719_power_up(dw9719); + if (ret) + return ret; + + for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus; + val += DW9719_CTRL_STEPS) { + ret = dw9719_t_focus_abs(dw9719, val); + if (ret) + goto err_power_down; + + usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10); + } + + return 0; + +err_power_down: + dw9719_power_down(dw9719); + return ret; +} + +static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return pm_runtime_resume_and_get(sd->dev); +} + +static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9719_internal_ops = { + .open = dw9719_open, + .close = dw9719_close, +}; + +static int dw9719_init_controls(struct dw9719_device *dw9719) +{ + const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops; + int ret; + + v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1); + + dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops, + V4L2_CID_FOCUS_ABSOLUTE, 0, + DW9719_MAX_FOCUS_POS, 1, 0); + + if (dw9719->ctrls.handler.error) { + dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n"); + ret = dw9719->ctrls.handler.error; + goto err_free_handler; + } + + dw9719->sd.ctrl_handler = &dw9719->ctrls.handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + return ret; +} + +static const struct v4l2_subdev_ops dw9719_ops = { }; + +static int dw9719_probe(struct i2c_client *client) +{ + struct dw9719_device *dw9719; + int ret; + + dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL); + if (!dw9719) + return -ENOMEM; + + dw9719->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(dw9719->regmap)) + return PTR_ERR(dw9719->regmap); + + dw9719->dev = &client->dev; + dw9719->sac_mode = DW9719_MODE_SAC3; + dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ; + + /* Optional indication of SAC mode select */ + device_property_read_u32(&client->dev, "dongwoon,sac-mode", + &dw9719->sac_mode); + + /* Optional indication of VCM frequency */ + device_property_read_u32(&client->dev, "dongwoon,vcm-freq", + &dw9719->vcm_freq); + + dw9719->regulator = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(dw9719->regulator)) + return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator), + "getting regulator\n"); + + v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops); + dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9719->sd.internal_ops = &dw9719_internal_ops; + + ret = dw9719_init_controls(dw9719); + if (ret) + return ret; + + ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL); + if (ret < 0) + goto err_free_ctrl_handler; + + dw9719->sd.entity.function = MEDIA_ENT_F_LENS; + + /* + * We need the driver to work in the event that pm runtime is disable in + * the kernel, so power up and verify the chip now. In the event that + * runtime pm is disabled this will leave the chip on, so that the lens + * will work. + */ + + ret = dw9719_power_up(dw9719); + if (ret) + goto err_cleanup_media; + + ret = dw9719_detect(dw9719); + if (ret) + goto err_powerdown; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + + ret = v4l2_async_register_subdev(&dw9719->sd); + if (ret < 0) + goto err_pm_runtime; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; + +err_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); +err_powerdown: + dw9719_power_down(dw9719); +err_cleanup_media: + media_entity_cleanup(&dw9719->sd.entity); +err_free_ctrl_handler: + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + + return ret; +} + +static void dw9719_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9719_device *dw9719 = + container_of(sd, struct dw9719_device, sd); + + v4l2_async_unregister_subdev(sd); + v4l2_ctrl_handler_free(&dw9719->ctrls.handler); + media_entity_cleanup(&dw9719->sd.entity); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + dw9719_power_down(dw9719); + pm_runtime_set_suspended(&client->dev); +} + +static const struct i2c_device_id dw9719_id_table[] = { + { "dw9719" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dw9719_id_table); + +static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume, + NULL); + +static struct i2c_driver dw9719_i2c_driver = { + .driver = { + .name = "dw9719", + .pm = pm_sleep_ptr(&dw9719_pm_ops), + }, + .probe = dw9719_probe, + .remove = dw9719_remove, + .id_table = dw9719_id_table, +}; +module_i2c_driver(dw9719_i2c_driver); + +MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>"); +MODULE_DESCRIPTION("DW9719 VCM Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/et8ek8/Kconfig b/drivers/media/i2c/et8ek8/Kconfig index 398dd4d21df1..987fc62d5e6b 100644 --- a/drivers/media/i2c/et8ek8/Kconfig +++ b/drivers/media/i2c/et8ek8/Kconfig @@ -1,10 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_ET8EK8 tristate "ET8EK8 camera sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select V4L2_FWNODE help This is a driver for the Toshiba ET8EK8 5 MP camera sensor. It is used for example in Nokia N900 (RX-51). diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index 50e78f5b058c..fd56ba138739 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -1357,6 +1357,6 @@ static struct i2c_driver hi556_i2c_driver = { module_i2c_driver(hi556_i2c_driver); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Hynix HI556 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c index 7cdce392e137..32547d7a2659 100644 --- a/drivers/media/i2c/hi847.c +++ b/drivers/media/i2c/hi847.c @@ -3005,6 +3005,6 @@ static struct i2c_driver hi847_i2c_driver = { module_i2c_driver(hi847_i2c_driver); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Hynix HI847 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c index 3e870fa9ff79..ee5a28675388 100644 --- a/drivers/media/i2c/imx208.c +++ b/drivers/media/i2c/imx208.c @@ -1109,6 +1109,6 @@ module_i2c_driver(imx208_i2c_driver); MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>"); MODULE_AUTHOR("Chen, Ping-chung <ping-chung.chen@intel.com>"); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("Sony IMX208 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index d737d5e9a4a6..a1136fdfbed2 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -345,7 +345,7 @@ static const char * const imx219_supply_name[] = { * - v flip * - h&v flips */ -static const u32 codes[] = { +static const u32 imx219_mbus_formats[] = { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, @@ -460,8 +460,6 @@ struct imx219 { struct v4l2_subdev sd; struct media_pad pad; - struct v4l2_mbus_framefmt fmt; - struct clk *xclk; /* system clock to IMX219 */ u32 xclk_freq; @@ -481,12 +479,6 @@ struct imx219 { /* Current mode */ const struct imx219_mode *mode; - /* - * Mutex for serialized access: - * Protect sensor module set pad format and start/stop streaming safely. - */ - struct mutex mutex; - /* Streaming on/off */ bool streaming; @@ -576,64 +568,17 @@ static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) { unsigned int i; - lockdep_assert_held(&imx219->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == code) + for (i = 0; i < ARRAY_SIZE(imx219_mbus_formats); i++) + if (imx219_mbus_formats[i] == code) break; - if (i >= ARRAY_SIZE(codes)) + if (i >= ARRAY_SIZE(imx219_mbus_formats)) i = 0; i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | (imx219->hflip->val ? 1 : 0); - return codes[i]; -} - -static void imx219_set_default_format(struct imx219 *imx219) -{ - struct v4l2_mbus_framefmt *fmt; - - fmt = &imx219->fmt; - fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = supported_modes[0].width; - fmt->height = supported_modes[0].height; - fmt->field = V4L2_FIELD_NONE; -} - -static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx219 *imx219 = to_imx219(sd); - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->state, 0); - struct v4l2_rect *try_crop; - - mutex_lock(&imx219->mutex); - - /* Initialize try_fmt */ - try_fmt->width = supported_modes[0].width; - try_fmt->height = supported_modes[0].height; - try_fmt->code = imx219_get_format_code(imx219, - MEDIA_BUS_FMT_SRGGB10_1X10); - try_fmt->field = V4L2_FIELD_NONE; - - /* Initialize try_crop rectangle. */ - try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); - try_crop->top = IMX219_PIXEL_ARRAY_TOP; - try_crop->left = IMX219_PIXEL_ARRAY_LEFT; - try_crop->width = IMX219_PIXEL_ARRAY_WIDTH; - try_crop->height = IMX219_PIXEL_ARRAY_HEIGHT; - - mutex_unlock(&imx219->mutex); - - return 0; + return imx219_mbus_formats[i]; } static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) @@ -725,18 +670,52 @@ static const struct v4l2_ctrl_ops imx219_ctrl_ops = { .s_ctrl = imx219_set_ctrl, }; +static void imx219_update_pad_format(struct imx219 *imx219, + const struct imx219_mode *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + /* Bayer order varies with flips */ + fmt->code = imx219_get_format_code(imx219, code); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int imx219_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx219 *imx219 = to_imx219(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + /* Initialize try_fmt */ + format = v4l2_subdev_get_pad_format(sd, state, 0); + imx219_update_pad_format(imx219, &supported_modes[0], format, + MEDIA_BUS_FMT_SRGGB10_1X10); + + /* Initialize crop rectangle. */ + crop = v4l2_subdev_get_pad_crop(sd, state, 0); + crop->top = IMX219_PIXEL_ARRAY_TOP; + crop->left = IMX219_PIXEL_ARRAY_LEFT; + crop->width = IMX219_PIXEL_ARRAY_WIDTH; + crop->height = IMX219_PIXEL_ARRAY_HEIGHT; + + return 0; +} + static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { struct imx219 *imx219 = to_imx219(sd); - if (code->index >= (ARRAY_SIZE(codes) / 4)) + if (code->index >= (ARRAY_SIZE(imx219_mbus_formats) / 4)) return -EINVAL; - mutex_lock(&imx219->mutex); - code->code = imx219_get_format_code(imx219, codes[code->index * 4]); - mutex_unlock(&imx219->mutex); + code->code = imx219_get_format_code(imx219, imx219_mbus_formats[code->index * 4]); return 0; } @@ -751,9 +730,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - mutex_lock(&imx219->mutex); code = imx219_get_format_code(imx219, fse->code); - mutex_unlock(&imx219->mutex); if (fse->code != code) return -EINVAL; @@ -765,92 +742,27 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) -{ - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, - fmt->colorspace, - fmt->ycbcr_enc); - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); -} - -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.field = V4L2_FIELD_NONE; - imx219_reset_colorspace(&fmt->format); -} - -static int __imx219_get_pad_format(struct imx219 *imx219, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(&imx219->sd, sd_state, - fmt->pad); - /* update the code which could change due to vflip or hflip: */ - try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); - fmt->format = *try_fmt; - } else { - imx219_update_pad_format(imx219, imx219->mode, fmt); - fmt->format.code = imx219_get_format_code(imx219, - imx219->fmt.code); - } - - return 0; -} - -static int imx219_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx219 *imx219 = to_imx219(sd); - int ret; - - mutex_lock(&imx219->mutex); - ret = __imx219_get_pad_format(imx219, sd_state, fmt); - mutex_unlock(&imx219->mutex); - - return ret; -} - static int imx219_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { struct imx219 *imx219 = to_imx219(sd); const struct imx219_mode *mode; - struct v4l2_mbus_framefmt *framefmt; int exposure_max, exposure_def, hblank; - unsigned int i; - - mutex_lock(&imx219->mutex); - - for (i = 0; i < ARRAY_SIZE(codes); i++) - if (codes[i] == fmt->format.code) - break; - if (i >= ARRAY_SIZE(codes)) - i = 0; - - /* Bayer order varies with flips */ - fmt->format.code = imx219_get_format_code(imx219, codes[i]); + struct v4l2_mbus_framefmt *format; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx219_update_pad_format(imx219, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); - *framefmt = fmt->format; - } else if (imx219->mode != mode || - imx219->fmt.code != fmt->format.code) { - imx219->fmt = fmt->format; + + imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + + if (imx219->mode == mode && format->code == fmt->format.code) + return 0; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx219->mode = mode; /* Update limits and set FPS to default */ __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, @@ -876,14 +788,15 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, hblank); } - mutex_unlock(&imx219->mutex); + *format = fmt->format; return 0; } -static int imx219_set_framefmt(struct imx219 *imx219) +static int imx219_set_framefmt(struct imx219 *imx219, + const struct v4l2_mbus_framefmt *format) { - switch (imx219->fmt.code) { + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: @@ -902,7 +815,8 @@ static int imx219_set_framefmt(struct imx219 *imx219) return -EINVAL; } -static int imx219_set_binning(struct imx219 *imx219) +static int imx219_set_binning(struct imx219 *imx219, + const struct v4l2_mbus_framefmt *format) { if (!imx219->mode->binning) { return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE, @@ -910,7 +824,7 @@ static int imx219_set_binning(struct imx219 *imx219) IMX219_BINNING_NONE); } - switch (imx219->fmt.code) { + switch (format->code) { case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: @@ -931,34 +845,13 @@ static int imx219_set_binning(struct imx219 *imx219) return -EINVAL; } -static const struct v4l2_rect * -__imx219_get_pad_crop(struct imx219 *imx219, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&imx219->sd, sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &imx219->mode->crop; - } - - return NULL; -} - static int imx219_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { case V4L2_SEL_TGT_CROP: { - struct imx219 *imx219 = to_imx219(sd); - - mutex_lock(&imx219->mutex); - sel->r = *__imx219_get_pad_crop(imx219, sd_state, sel->pad, - sel->which); - mutex_unlock(&imx219->mutex); - + sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0); return 0; } @@ -990,9 +883,11 @@ static int imx219_configure_lanes(struct imx219 *imx219) IMX219_CSI_2_LANE_MODE : IMX219_CSI_4_LANE_MODE); }; -static int imx219_start_streaming(struct imx219 *imx219) +static int imx219_start_streaming(struct imx219 *imx219, + struct v4l2_subdev_state *state) { struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct v4l2_mbus_framefmt *format; const struct imx219_reg_list *reg_list; int ret; @@ -1022,14 +917,15 @@ static int imx219_start_streaming(struct imx219 *imx219) goto err_rpm_put; } - ret = imx219_set_framefmt(imx219); + format = v4l2_subdev_get_pad_format(&imx219->sd, state, 0); + ret = imx219_set_framefmt(imx219, format); if (ret) { dev_err(&client->dev, "%s failed to set frame format: %d\n", __func__, ret); goto err_rpm_put; } - ret = imx219_set_binning(imx219); + ret = imx219_set_binning(imx219, format); if (ret) { dev_err(&client->dev, "%s failed to set binning: %d\n", __func__, ret); @@ -1078,35 +974,30 @@ static void imx219_stop_streaming(struct imx219 *imx219) static int imx219_set_stream(struct v4l2_subdev *sd, int enable) { struct imx219 *imx219 = to_imx219(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx219->mutex); - if (imx219->streaming == enable) { - mutex_unlock(&imx219->mutex); - return 0; - } + state = v4l2_subdev_lock_and_get_active_state(sd); + + if (imx219->streaming == enable) + goto unlock; if (enable) { /* * Apply default & customized values * and then start streaming. */ - ret = imx219_start_streaming(imx219); + ret = imx219_start_streaming(imx219, state); if (ret) - goto err_unlock; + goto unlock; } else { imx219_stop_streaming(imx219); } imx219->streaming = enable; - mutex_unlock(&imx219->mutex); - - return ret; - -err_unlock: - mutex_unlock(&imx219->mutex); - +unlock: + v4l2_subdev_unlock_state(state); return ret; } @@ -1171,10 +1062,13 @@ static int __maybe_unused imx219_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); + struct v4l2_subdev_state *state; int ret; if (imx219->streaming) { - ret = imx219_start_streaming(imx219); + state = v4l2_subdev_lock_and_get_active_state(sd); + ret = imx219_start_streaming(imx219, state); + v4l2_subdev_unlock_state(state); if (ret) goto error; } @@ -1235,8 +1129,9 @@ static const struct v4l2_subdev_video_ops imx219_video_ops = { }; static const struct v4l2_subdev_pad_ops imx219_pad_ops = { + .init_cfg = imx219_init_cfg, .enum_mbus_code = imx219_enum_mbus_code, - .get_fmt = imx219_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx219_set_pad_format, .get_selection = imx219_get_selection, .enum_frame_size = imx219_enum_frame_size, @@ -1248,9 +1143,6 @@ static const struct v4l2_subdev_ops imx219_subdev_ops = { .pad = &imx219_pad_ops, }; -static const struct v4l2_subdev_internal_ops imx219_internal_ops = { - .open = imx219_open, -}; static unsigned long imx219_get_pixel_rate(struct imx219 *imx219) { @@ -1272,9 +1164,6 @@ static int imx219_init_controls(struct imx219 *imx219) if (ret) return ret; - mutex_init(&imx219->mutex); - ctrl_hdlr->lock = &imx219->mutex; - /* By default, PIXEL_RATE is read only */ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1371,7 +1260,6 @@ static int imx219_init_controls(struct imx219 *imx219) error: v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&imx219->mutex); return ret; } @@ -1379,7 +1267,6 @@ error: static void imx219_free_controls(struct imx219 *imx219) { v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); - mutex_destroy(&imx219->mutex); } static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) @@ -1509,7 +1396,6 @@ static int imx219_probe(struct i2c_client *client) goto error_power_off; /* Initialize subdev */ - imx219->sd.internal_ops = &imx219_internal_ops; imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1517,19 +1403,23 @@ static int imx219_probe(struct i2c_client *client) /* Initialize source pad */ imx219->pad.flags = MEDIA_PAD_FL_SOURCE; - /* Initialize default format */ - imx219_set_default_format(imx219); - ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); if (ret) { dev_err(dev, "failed to init entity pads: %d\n", ret); goto error_handler_free; } + imx219->sd.state_lock = imx219->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx219->sd); + if (ret < 0) { + dev_err(dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + ret = v4l2_async_register_subdev_sensor(&imx219->sd); if (ret < 0) { dev_err(dev, "failed to register sensor sub-device: %d\n", ret); - goto error_media_entity; + goto error_subdev_cleanup; } /* Enable runtime PM and turn off the device */ @@ -1539,6 +1429,9 @@ static int imx219_probe(struct i2c_client *client) return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx219->sd); + error_media_entity: media_entity_cleanup(&imx219->sd.entity); @@ -1557,6 +1450,7 @@ static void imx219_remove(struct i2c_client *client) struct imx219 *imx219 = to_imx219(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); imx219_free_controls(imx219); diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index b3f832e9d7e1..29098612813c 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -13,7 +13,7 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -21,91 +21,86 @@ #include <asm/unaligned.h> #include <media/media-entity.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define IMX290_REG_SIZE_SHIFT 16 -#define IMX290_REG_ADDR_MASK 0xffff -#define IMX290_REG_8BIT(n) ((1U << IMX290_REG_SIZE_SHIFT) | (n)) -#define IMX290_REG_16BIT(n) ((2U << IMX290_REG_SIZE_SHIFT) | (n)) -#define IMX290_REG_24BIT(n) ((3U << IMX290_REG_SIZE_SHIFT) | (n)) - -#define IMX290_STANDBY IMX290_REG_8BIT(0x3000) -#define IMX290_REGHOLD IMX290_REG_8BIT(0x3001) -#define IMX290_XMSTA IMX290_REG_8BIT(0x3002) -#define IMX290_ADBIT IMX290_REG_8BIT(0x3005) +#define IMX290_STANDBY CCI_REG8(0x3000) +#define IMX290_REGHOLD CCI_REG8(0x3001) +#define IMX290_XMSTA CCI_REG8(0x3002) +#define IMX290_ADBIT CCI_REG8(0x3005) #define IMX290_ADBIT_10BIT (0 << 0) #define IMX290_ADBIT_12BIT (1 << 0) -#define IMX290_CTRL_07 IMX290_REG_8BIT(0x3007) +#define IMX290_CTRL_07 CCI_REG8(0x3007) #define IMX290_VREVERSE BIT(0) #define IMX290_HREVERSE BIT(1) #define IMX290_WINMODE_1080P (0 << 4) #define IMX290_WINMODE_720P (1 << 4) #define IMX290_WINMODE_CROP (4 << 4) -#define IMX290_FR_FDG_SEL IMX290_REG_8BIT(0x3009) -#define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) -#define IMX290_GAIN IMX290_REG_8BIT(0x3014) -#define IMX290_VMAX IMX290_REG_24BIT(0x3018) +#define IMX290_FR_FDG_SEL CCI_REG8(0x3009) +#define IMX290_BLKLEVEL CCI_REG16(0x300a) +#define IMX290_GAIN CCI_REG8(0x3014) +#define IMX290_VMAX CCI_REG24(0x3018) #define IMX290_VMAX_MAX 0x3ffff -#define IMX290_HMAX IMX290_REG_16BIT(0x301c) +#define IMX290_HMAX CCI_REG16(0x301c) #define IMX290_HMAX_MAX 0xffff -#define IMX290_SHS1 IMX290_REG_24BIT(0x3020) -#define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) -#define IMX290_WINPV IMX290_REG_16BIT(0x303c) -#define IMX290_WINWV IMX290_REG_16BIT(0x303e) -#define IMX290_WINPH IMX290_REG_16BIT(0x3040) -#define IMX290_WINWH IMX290_REG_16BIT(0x3042) -#define IMX290_OUT_CTRL IMX290_REG_8BIT(0x3046) +#define IMX290_SHS1 CCI_REG24(0x3020) +#define IMX290_WINWV_OB CCI_REG8(0x303a) +#define IMX290_WINPV CCI_REG16(0x303c) +#define IMX290_WINWV CCI_REG16(0x303e) +#define IMX290_WINPH CCI_REG16(0x3040) +#define IMX290_WINWH CCI_REG16(0x3042) +#define IMX290_OUT_CTRL CCI_REG8(0x3046) #define IMX290_ODBIT_10BIT (0 << 0) #define IMX290_ODBIT_12BIT (1 << 0) #define IMX290_OPORTSEL_PARALLEL (0x0 << 4) #define IMX290_OPORTSEL_LVDS_2CH (0xd << 4) #define IMX290_OPORTSEL_LVDS_4CH (0xe << 4) #define IMX290_OPORTSEL_LVDS_8CH (0xf << 4) -#define IMX290_XSOUTSEL IMX290_REG_8BIT(0x304b) +#define IMX290_XSOUTSEL CCI_REG8(0x304b) #define IMX290_XSOUTSEL_XVSOUTSEL_HIGH (0 << 0) #define IMX290_XSOUTSEL_XVSOUTSEL_VSYNC (2 << 0) #define IMX290_XSOUTSEL_XHSOUTSEL_HIGH (0 << 2) #define IMX290_XSOUTSEL_XHSOUTSEL_HSYNC (2 << 2) -#define IMX290_INCKSEL1 IMX290_REG_8BIT(0x305c) -#define IMX290_INCKSEL2 IMX290_REG_8BIT(0x305d) -#define IMX290_INCKSEL3 IMX290_REG_8BIT(0x305e) -#define IMX290_INCKSEL4 IMX290_REG_8BIT(0x305f) -#define IMX290_PGCTRL IMX290_REG_8BIT(0x308c) -#define IMX290_ADBIT1 IMX290_REG_8BIT(0x3129) +#define IMX290_INCKSEL1 CCI_REG8(0x305c) +#define IMX290_INCKSEL2 CCI_REG8(0x305d) +#define IMX290_INCKSEL3 CCI_REG8(0x305e) +#define IMX290_INCKSEL4 CCI_REG8(0x305f) +#define IMX290_PGCTRL CCI_REG8(0x308c) +#define IMX290_ADBIT1 CCI_REG8(0x3129) #define IMX290_ADBIT1_10BIT 0x1d #define IMX290_ADBIT1_12BIT 0x00 -#define IMX290_INCKSEL5 IMX290_REG_8BIT(0x315e) -#define IMX290_INCKSEL6 IMX290_REG_8BIT(0x3164) -#define IMX290_ADBIT2 IMX290_REG_8BIT(0x317c) +#define IMX290_INCKSEL5 CCI_REG8(0x315e) +#define IMX290_INCKSEL6 CCI_REG8(0x3164) +#define IMX290_ADBIT2 CCI_REG8(0x317c) #define IMX290_ADBIT2_10BIT 0x12 #define IMX290_ADBIT2_12BIT 0x00 -#define IMX290_CHIP_ID IMX290_REG_16BIT(0x319a) -#define IMX290_ADBIT3 IMX290_REG_8BIT(0x31ec) +#define IMX290_CHIP_ID CCI_REG16(0x319a) +#define IMX290_ADBIT3 CCI_REG8(0x31ec) #define IMX290_ADBIT3_10BIT 0x37 #define IMX290_ADBIT3_12BIT 0x0e -#define IMX290_REPETITION IMX290_REG_8BIT(0x3405) -#define IMX290_PHY_LANE_NUM IMX290_REG_8BIT(0x3407) -#define IMX290_OPB_SIZE_V IMX290_REG_8BIT(0x3414) -#define IMX290_Y_OUT_SIZE IMX290_REG_16BIT(0x3418) -#define IMX290_CSI_DT_FMT IMX290_REG_16BIT(0x3441) +#define IMX290_REPETITION CCI_REG8(0x3405) +#define IMX290_PHY_LANE_NUM CCI_REG8(0x3407) +#define IMX290_OPB_SIZE_V CCI_REG8(0x3414) +#define IMX290_Y_OUT_SIZE CCI_REG16(0x3418) +#define IMX290_CSI_DT_FMT CCI_REG16(0x3441) #define IMX290_CSI_DT_FMT_RAW10 0x0a0a #define IMX290_CSI_DT_FMT_RAW12 0x0c0c -#define IMX290_CSI_LANE_MODE IMX290_REG_8BIT(0x3443) -#define IMX290_EXTCK_FREQ IMX290_REG_16BIT(0x3444) -#define IMX290_TCLKPOST IMX290_REG_16BIT(0x3446) -#define IMX290_THSZERO IMX290_REG_16BIT(0x3448) -#define IMX290_THSPREPARE IMX290_REG_16BIT(0x344a) -#define IMX290_TCLKTRAIL IMX290_REG_16BIT(0x344c) -#define IMX290_THSTRAIL IMX290_REG_16BIT(0x344e) -#define IMX290_TCLKZERO IMX290_REG_16BIT(0x3450) -#define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) -#define IMX290_TLPX IMX290_REG_16BIT(0x3454) -#define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) -#define IMX290_INCKSEL7 IMX290_REG_8BIT(0x3480) +#define IMX290_CSI_LANE_MODE CCI_REG8(0x3443) +#define IMX290_EXTCK_FREQ CCI_REG16(0x3444) +#define IMX290_TCLKPOST CCI_REG16(0x3446) +#define IMX290_THSZERO CCI_REG16(0x3448) +#define IMX290_THSPREPARE CCI_REG16(0x344a) +#define IMX290_TCLKTRAIL CCI_REG16(0x344c) +#define IMX290_THSTRAIL CCI_REG16(0x344e) +#define IMX290_TCLKZERO CCI_REG16(0x3450) +#define IMX290_TCLKPREPARE CCI_REG16(0x3452) +#define IMX290_TLPX CCI_REG16(0x3454) +#define IMX290_X_OUT_SIZE CCI_REG16(0x3472) +#define IMX290_INCKSEL7 CCI_REG8(0x3480) #define IMX290_PGCTRL_REGEN BIT(0) #define IMX290_PGCTRL_THRU BIT(1) @@ -181,7 +176,7 @@ enum imx290_model { struct imx290_model_info { enum imx290_colour_variant colour_variant; - const struct imx290_regval *init_regs; + const struct cci_reg_sequence *init_regs; size_t init_regs_num; const char *name; }; @@ -192,11 +187,6 @@ enum imx290_clk_freq { IMX290_NUM_CLK }; -struct imx290_regval { - u32 reg; - u32 val; -}; - /* * Clock configuration for registers INCKSEL1 to INCKSEL6. */ @@ -217,7 +207,7 @@ struct imx290_mode { u8 link_freq_index; u8 ctrl_07; - const struct imx290_regval *data; + const struct cci_reg_sequence *data; u32 data_size; const struct imx290_clk_cfg *clk_cfg; @@ -271,7 +261,7 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) * Modes and formats */ -static const struct imx290_regval imx290_global_init_settings[] = { +static const struct cci_reg_sequence imx290_global_init_settings[] = { { IMX290_WINWV_OB, 12 }, { IMX290_WINPH, 0 }, { IMX290_WINPV, 0 }, @@ -279,56 +269,56 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { IMX290_REG_8BIT(0x3011), 0x02 }, - { IMX290_REG_8BIT(0x3012), 0x64 }, - { IMX290_REG_8BIT(0x3013), 0x00 }, + { CCI_REG8(0x3011), 0x02 }, + { CCI_REG8(0x3012), 0x64 }, + { CCI_REG8(0x3013), 0x00 }, }; -static const struct imx290_regval imx290_global_init_settings_290[] = { - { IMX290_REG_8BIT(0x300f), 0x00 }, - { IMX290_REG_8BIT(0x3010), 0x21 }, - { IMX290_REG_8BIT(0x3016), 0x09 }, - { IMX290_REG_8BIT(0x3070), 0x02 }, - { IMX290_REG_8BIT(0x3071), 0x11 }, - { IMX290_REG_8BIT(0x309b), 0x10 }, - { IMX290_REG_8BIT(0x309c), 0x22 }, - { IMX290_REG_8BIT(0x30a2), 0x02 }, - { IMX290_REG_8BIT(0x30a6), 0x20 }, - { IMX290_REG_8BIT(0x30a8), 0x20 }, - { IMX290_REG_8BIT(0x30aa), 0x20 }, - { IMX290_REG_8BIT(0x30ac), 0x20 }, - { IMX290_REG_8BIT(0x30b0), 0x43 }, - { IMX290_REG_8BIT(0x3119), 0x9e }, - { IMX290_REG_8BIT(0x311c), 0x1e }, - { IMX290_REG_8BIT(0x311e), 0x08 }, - { IMX290_REG_8BIT(0x3128), 0x05 }, - { IMX290_REG_8BIT(0x313d), 0x83 }, - { IMX290_REG_8BIT(0x3150), 0x03 }, - { IMX290_REG_8BIT(0x317e), 0x00 }, - { IMX290_REG_8BIT(0x32b8), 0x50 }, - { IMX290_REG_8BIT(0x32b9), 0x10 }, - { IMX290_REG_8BIT(0x32ba), 0x00 }, - { IMX290_REG_8BIT(0x32bb), 0x04 }, - { IMX290_REG_8BIT(0x32c8), 0x50 }, - { IMX290_REG_8BIT(0x32c9), 0x10 }, - { IMX290_REG_8BIT(0x32ca), 0x00 }, - { IMX290_REG_8BIT(0x32cb), 0x04 }, - { IMX290_REG_8BIT(0x332c), 0xd3 }, - { IMX290_REG_8BIT(0x332d), 0x10 }, - { IMX290_REG_8BIT(0x332e), 0x0d }, - { IMX290_REG_8BIT(0x3358), 0x06 }, - { IMX290_REG_8BIT(0x3359), 0xe1 }, - { IMX290_REG_8BIT(0x335a), 0x11 }, - { IMX290_REG_8BIT(0x3360), 0x1e }, - { IMX290_REG_8BIT(0x3361), 0x61 }, - { IMX290_REG_8BIT(0x3362), 0x10 }, - { IMX290_REG_8BIT(0x33b0), 0x50 }, - { IMX290_REG_8BIT(0x33b2), 0x1a }, - { IMX290_REG_8BIT(0x33b3), 0x04 }, +static const struct cci_reg_sequence imx290_global_init_settings_290[] = { + { CCI_REG8(0x300f), 0x00 }, + { CCI_REG8(0x3010), 0x21 }, + { CCI_REG8(0x3016), 0x09 }, + { CCI_REG8(0x3070), 0x02 }, + { CCI_REG8(0x3071), 0x11 }, + { CCI_REG8(0x309b), 0x10 }, + { CCI_REG8(0x309c), 0x22 }, + { CCI_REG8(0x30a2), 0x02 }, + { CCI_REG8(0x30a6), 0x20 }, + { CCI_REG8(0x30a8), 0x20 }, + { CCI_REG8(0x30aa), 0x20 }, + { CCI_REG8(0x30ac), 0x20 }, + { CCI_REG8(0x30b0), 0x43 }, + { CCI_REG8(0x3119), 0x9e }, + { CCI_REG8(0x311c), 0x1e }, + { CCI_REG8(0x311e), 0x08 }, + { CCI_REG8(0x3128), 0x05 }, + { CCI_REG8(0x313d), 0x83 }, + { CCI_REG8(0x3150), 0x03 }, + { CCI_REG8(0x317e), 0x00 }, + { CCI_REG8(0x32b8), 0x50 }, + { CCI_REG8(0x32b9), 0x10 }, + { CCI_REG8(0x32ba), 0x00 }, + { CCI_REG8(0x32bb), 0x04 }, + { CCI_REG8(0x32c8), 0x50 }, + { CCI_REG8(0x32c9), 0x10 }, + { CCI_REG8(0x32ca), 0x00 }, + { CCI_REG8(0x32cb), 0x04 }, + { CCI_REG8(0x332c), 0xd3 }, + { CCI_REG8(0x332d), 0x10 }, + { CCI_REG8(0x332e), 0x0d }, + { CCI_REG8(0x3358), 0x06 }, + { CCI_REG8(0x3359), 0xe1 }, + { CCI_REG8(0x335a), 0x11 }, + { CCI_REG8(0x3360), 0x1e }, + { CCI_REG8(0x3361), 0x61 }, + { CCI_REG8(0x3362), 0x10 }, + { CCI_REG8(0x33b0), 0x50 }, + { CCI_REG8(0x33b2), 0x1a }, + { CCI_REG8(0x33b3), 0x04 }, }; #define IMX290_NUM_CLK_REGS 2 -static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { +static const struct cci_reg_sequence xclk_regs[][IMX290_NUM_CLK_REGS] = { [IMX290_CLK_37_125] = { { IMX290_EXTCK_FREQ, (37125 * 256) / 1000 }, { IMX290_INCKSEL7, 0x49 }, @@ -339,13 +329,13 @@ static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { }, }; -static const struct imx290_regval imx290_global_init_settings_327[] = { - { IMX290_REG_8BIT(0x309e), 0x4A }, - { IMX290_REG_8BIT(0x309f), 0x4A }, - { IMX290_REG_8BIT(0x313b), 0x61 }, +static const struct cci_reg_sequence imx290_global_init_settings_327[] = { + { CCI_REG8(0x309e), 0x4A }, + { CCI_REG8(0x309f), 0x4A }, + { CCI_REG8(0x313b), 0x61 }, }; -static const struct imx290_regval imx290_1080p_settings[] = { +static const struct cci_reg_sequence imx290_1080p_settings[] = { /* mode settings */ { IMX290_WINWV_OB, 12 }, { IMX290_OPB_SIZE_V, 10 }, @@ -353,7 +343,7 @@ static const struct imx290_regval imx290_1080p_settings[] = { { IMX290_Y_OUT_SIZE, 1080 }, }; -static const struct imx290_regval imx290_720p_settings[] = { +static const struct cci_reg_sequence imx290_720p_settings[] = { /* mode settings */ { IMX290_WINWV_OB, 6 }, { IMX290_OPB_SIZE_V, 4 }, @@ -361,7 +351,7 @@ static const struct imx290_regval imx290_720p_settings[] = { { IMX290_Y_OUT_SIZE, 720 }, }; -static const struct imx290_regval imx290_10bit_settings[] = { +static const struct cci_reg_sequence imx290_10bit_settings[] = { { IMX290_ADBIT, IMX290_ADBIT_10BIT }, { IMX290_OUT_CTRL, IMX290_ODBIT_10BIT }, { IMX290_ADBIT1, IMX290_ADBIT1_10BIT }, @@ -370,7 +360,7 @@ static const struct imx290_regval imx290_10bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW10 }, }; -static const struct imx290_regval imx290_12bit_settings[] = { +static const struct cci_reg_sequence imx290_12bit_settings[] = { { IMX290_ADBIT, IMX290_ADBIT_12BIT }, { IMX290_OUT_CTRL, IMX290_ODBIT_12BIT }, { IMX290_ADBIT1, IMX290_ADBIT1_12BIT }, @@ -576,7 +566,7 @@ static inline int imx290_modes_num(const struct imx290 *imx290) struct imx290_format_info { u32 code[IMX290_VARIANT_MAX]; u8 bpp; - const struct imx290_regval *regs; + const struct cci_reg_sequence *regs; unsigned int num_regs; }; @@ -615,63 +605,15 @@ imx290_format_info(const struct imx290 *imx290, u32 code) return NULL; } -/* ----------------------------------------------------------------------------- - * Register access - */ - -static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *value) -{ - u8 data[3] = { 0, 0, 0 }; - int ret; - - ret = regmap_raw_read(imx290->regmap, addr & IMX290_REG_ADDR_MASK, - data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); - if (ret < 0) { - dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, - addr & IMX290_REG_ADDR_MASK, ret); - return ret; - } - - *value = get_unaligned_le24(data); - return 0; -} - -static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) -{ - u8 data[3]; - int ret; - - if (err && *err) - return *err; - - put_unaligned_le24(value, data); - - ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, - data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); - if (ret < 0) { - dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, - addr & IMX290_REG_ADDR_MASK, ret); - if (err) - *err = ret; - } - - return ret; -} - static int imx290_set_register_array(struct imx290 *imx290, - const struct imx290_regval *settings, + const struct cci_reg_sequence *settings, unsigned int num_settings) { - unsigned int i; int ret; - for (i = 0; i < num_settings; ++i, ++settings) { - ret = imx290_write(imx290, settings->reg, settings->val, NULL); - if (ret < 0) - return ret; - } + ret = cci_multi_reg_write(imx290->regmap, settings, num_settings, NULL); + if (ret < 0) + return ret; /* Provide 10ms settle time */ usleep_range(10000, 11000); @@ -689,12 +631,12 @@ static int imx290_set_clock(struct imx290 *imx290) ret = imx290_set_register_array(imx290, xclk_regs[clk_idx], IMX290_NUM_CLK_REGS); - imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); - imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); - imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); - imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); - imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); - imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); + cci_write(imx290->regmap, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); return ret; } @@ -703,9 +645,11 @@ static int imx290_set_data_lanes(struct imx290 *imx290) { int ret = 0; - imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret); + cci_write(imx290->regmap, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, + &ret); + cci_write(imx290->regmap, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, + &ret); + cci_write(imx290->regmap, IMX290_FR_FDG_SEL, 0x01, &ret); return ret; } @@ -716,8 +660,8 @@ static int imx290_set_black_level(struct imx290 *imx290, { unsigned int bpp = imx290_format_info(imx290, format->code)->bpp; - return imx290_write(imx290, IMX290_BLKLEVEL, - black_level >> (16 - bpp), err); + return cci_write(imx290->regmap, IMX290_BLKLEVEL, + black_level >> (16 - bpp), err); } static int imx290_set_csi_config(struct imx290 *imx290) @@ -743,15 +687,16 @@ static int imx290_set_csi_config(struct imx290 *imx290) return -EINVAL; } - imx290_write(imx290, IMX290_REPETITION, csi_cfg->repetition, &ret); - imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); - imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret); - imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); - imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); - imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); - imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); - imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret); - imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret); + cci_write(imx290->regmap, IMX290_REPETITION, csi_cfg->repetition, &ret); + cci_write(imx290->regmap, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); + cci_write(imx290->regmap, IMX290_THSZERO, csi_cfg->thszero, &ret); + cci_write(imx290->regmap, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); + cci_write(imx290->regmap, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); + cci_write(imx290->regmap, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); + cci_write(imx290->regmap, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); + cci_write(imx290->regmap, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, + &ret); + cci_write(imx290->regmap, IMX290_TLPX, csi_cfg->tlpx, &ret); return ret; } @@ -817,13 +762,12 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); + ret = cci_write(imx290->regmap, IMX290_GAIN, ctrl->val, NULL); break; case V4L2_CID_VBLANK: - ret = imx290_write(imx290, IMX290_VMAX, - ctrl->val + imx290->current_mode->height, - NULL); + ret = cci_write(imx290->regmap, IMX290_VMAX, + ctrl->val + imx290->current_mode->height, NULL); /* * Due to the way that exposure is programmed in this sensor in * relation to VMAX, we have to reprogramme it whenever VMAX is @@ -835,20 +779,20 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) fallthrough; case V4L2_CID_EXPOSURE: vmax = imx290->vblank->val + imx290->current_mode->height; - ret = imx290_write(imx290, IMX290_SHS1, - vmax - ctrl->val - 1, NULL); + ret = cci_write(imx290->regmap, IMX290_SHS1, + vmax - ctrl->val - 1, NULL); break; case V4L2_CID_TEST_PATTERN: if (ctrl->val) { imx290_set_black_level(imx290, format, 0, &ret); usleep_range(10000, 11000); - imx290_write(imx290, IMX290_PGCTRL, - (u8)(IMX290_PGCTRL_REGEN | - IMX290_PGCTRL_THRU | - IMX290_PGCTRL_MODE(ctrl->val)), &ret); + cci_write(imx290->regmap, IMX290_PGCTRL, + (u8)(IMX290_PGCTRL_REGEN | + IMX290_PGCTRL_THRU | + IMX290_PGCTRL_MODE(ctrl->val)), &ret); } else { - imx290_write(imx290, IMX290_PGCTRL, 0x00, &ret); + cci_write(imx290->regmap, IMX290_PGCTRL, 0x00, &ret); usleep_range(10000, 11000); imx290_set_black_level(imx290, format, IMX290_BLACK_LEVEL_DEFAULT, &ret); @@ -856,9 +800,8 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_HBLANK: - ret = imx290_write(imx290, IMX290_HMAX, - ctrl->val + imx290->current_mode->width, - NULL); + ret = cci_write(imx290->regmap, IMX290_HMAX, + ctrl->val + imx290->current_mode->width, NULL); break; case V4L2_CID_HFLIP: @@ -871,7 +814,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) reg |= IMX290_HREVERSE; if (imx290->vflip->val) reg |= IMX290_VREVERSE; - ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL); + ret = cci_write(imx290->regmap, IMX290_CTRL_07, reg, NULL); break; } @@ -902,7 +845,6 @@ static const char * const imx290_test_pattern_menu[] = { }; static void imx290_ctrl_update(struct imx290 *imx290, - const struct v4l2_mbus_framefmt *format, const struct imx290_mode *mode) { unsigned int hblank_min = mode->hmax_min - mode->width; @@ -1074,12 +1016,12 @@ static int imx290_start_streaming(struct imx290 *imx290, return ret; } - imx290_write(imx290, IMX290_STANDBY, 0x00, &ret); + cci_write(imx290->regmap, IMX290_STANDBY, 0x00, &ret); msleep(30); /* Start streaming */ - return imx290_write(imx290, IMX290_XMSTA, 0x00, &ret); + return cci_write(imx290->regmap, IMX290_XMSTA, 0x00, &ret); } /* Stop streaming */ @@ -1087,11 +1029,11 @@ static int imx290_stop_streaming(struct imx290 *imx290) { int ret = 0; - imx290_write(imx290, IMX290_STANDBY, 0x01, &ret); + cci_write(imx290->regmap, IMX290_STANDBY, 0x01, &ret); msleep(30); - return imx290_write(imx290, IMX290_XMSTA, 0x01, &ret); + return cci_write(imx290->regmap, IMX290_XMSTA, 0x01, &ret); } static int imx290_set_stream(struct v4l2_subdev *sd, int enable) @@ -1195,7 +1137,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx290->current_mode = mode; - imx290_ctrl_update(imx290, &fmt->format, mode); + imx290_ctrl_update(imx290, mode); imx290_exposure_update(imx290, mode); } @@ -1300,7 +1242,6 @@ static const struct media_entity_operations imx290_subdev_entity_ops = { static int imx290_subdev_init(struct imx290 *imx290) { struct i2c_client *client = to_i2c_client(imx290->dev); - const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; int ret; @@ -1335,8 +1276,7 @@ static int imx290_subdev_init(struct imx290 *imx290) } state = v4l2_subdev_lock_and_get_active_state(&imx290->sd); - format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); - imx290_ctrl_update(imx290, format, imx290->current_mode); + imx290_ctrl_update(imx290, imx290->current_mode); v4l2_subdev_unlock_state(state); return 0; @@ -1417,11 +1357,6 @@ static const struct dev_pm_ops imx290_pm_ops = { * Probe & remove */ -static const struct regmap_config imx290_regmap_config = { - .reg_bits = 16, - .val_bits = 8, -}; - static const char * const imx290_supply_name[IMX290_NUM_SUPPLIES] = { "vdda", "vddd", @@ -1588,7 +1523,7 @@ static int imx290_probe(struct i2c_client *client) return -ENOMEM; imx290->dev = dev; - imx290->regmap = devm_regmap_init_i2c(client, &imx290_regmap_config); + imx290->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx290->regmap)) { dev_err(dev, "Unable to initialize I2C\n"); return -ENODEV; diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index c0b9a5349668..3b4539b622b4 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -9,7 +9,7 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index a2140848d0d6..52ebb096e107 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -2565,7 +2565,7 @@ static struct i2c_driver imx319_i2c_driver = { module_i2c_driver(imx319_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); -MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Sony imx319 sensor driver"); diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 6571a98b1e9e..9c79ae8dc842 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1851,7 +1851,7 @@ static struct i2c_driver imx355_i2c_driver = { module_i2c_driver(imx355_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); -MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Sony imx355 sensor driver"); diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 4b5d1ee9cc6b..3f00172df3cc 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -9,7 +9,7 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index 92e49d95363d..73460688c356 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1611,7 +1611,7 @@ static const struct dev_pm_ops isl7998x_pm_ops = { static struct i2c_driver isl7998x_i2c_driver = { .driver = { .name = "isl7998x", - .of_match_table = of_match_ptr(isl7998x_of_match), + .of_match_table = isl7998x_of_match, .pm = &isl7998x_pm_ops, }, .probe = isl7998x_probe, diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 88c58e0c49aa..20e7c7cf5eeb 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -161,11 +161,12 @@ struct max9286_source { }; struct max9286_asd { - struct v4l2_async_subdev base; + struct v4l2_async_connection base; struct max9286_source *source; }; -static inline struct max9286_asd *to_max9286_asd(struct v4l2_async_subdev *asd) +static inline struct max9286_asd * +to_max9286_asd(struct v4l2_async_connection *asd) { return container_of(asd, struct max9286_asd, base); } @@ -659,7 +660,7 @@ static int max9286_set_pixelrate(struct max9286_priv *priv) static int max9286_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); struct max9286_source *source = to_max9286_asd(asd)->source; @@ -721,7 +722,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier, static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); struct max9286_source *source = to_max9286_asd(asd)->source; @@ -745,7 +746,7 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) if (!priv->nsources) return 0; - v4l2_async_nf_init(&priv->notifier); + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); for_each_source(priv, source) { unsigned int i = to_index(priv, source); @@ -765,7 +766,7 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) priv->notifier.ops = &max9286_notify_ops; - ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); + ret = v4l2_async_nf_register(&priv->notifier); if (ret) { dev_err(dev, "Failed to register subdev_notifier"); v4l2_async_nf_cleanup(&priv->notifier); @@ -1051,7 +1052,6 @@ static const struct v4l2_ctrl_ops max9286_ctrl_ops = { static int max9286_v4l2_register(struct max9286_priv *priv) { struct device *dev = &priv->client->dev; - struct fwnode_handle *ep; int ret; int i; @@ -1093,25 +1093,14 @@ static int max9286_v4l2_register(struct max9286_priv *priv) if (ret) goto err_async; - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD, - 0, 0); - if (!ep) { - dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n"); - ret = -ENOENT; - goto err_async; - } - priv->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { dev_err(dev, "Unable to register subdevice\n"); - goto err_put_node; + goto err_async; } return 0; -err_put_node: - fwnode_handle_put(ep); err_async: v4l2_ctrl_handler_free(&priv->ctrls); max9286_v4l2_notifier_unregister(priv); @@ -1714,7 +1703,7 @@ MODULE_DEVICE_TABLE(of, max9286_dt_ids); static struct i2c_driver max9286_i2c_driver = { .driver = { .name = "max9286", - .of_match_table = of_match_ptr(max9286_dt_ids), + .of_match_table = max9286_dt_ids, }, .probe = max9286_probe, .remove = max9286_remove, diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 2878d328fc01..df8d9c9e6a96 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1382,7 +1382,7 @@ MODULE_DEVICE_TABLE(i2c, mt9m111_id); static struct i2c_driver mt9m111_i2c_driver = { .driver = { .name = "mt9m111", - .of_match_table = of_match_ptr(mt9m111_of_match), + .of_match_table = mt9m111_of_match, }, .probe = mt9m111_probe, .remove = mt9m111_remove, diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index b5948759342e..365ce5684583 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -1121,6 +1121,6 @@ static struct i2c_driver og01a1b_i2c_driver = { module_i2c_driver(og01a1b_i2c_driver); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OG01A1B sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index de5bc19e715b..2b9e1b3a3bf4 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -992,7 +992,7 @@ static struct i2c_driver ov01a10_i2c_driver = { .pm = &ov01a10_pm_ops, .acpi_match_table = ACPI_PTR(ov01a10_acpi_ids), }, - .probe_new = ov01a10_probe, + .probe = ov01a10_probe, .remove = ov01a10_remove, }; diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c index 77bcdcd0824c..637da4df6901 100644 --- a/drivers/media/i2c/ov08x40.c +++ b/drivers/media/i2c/ov08x40.c @@ -110,8 +110,6 @@ struct ov08x40_reg_list { /* Link frequency config */ struct ov08x40_link_freq_config { - u32 pixels_per_line; - /* registers for this link frequency */ struct ov08x40_reg_list reg_list; }; @@ -128,6 +126,9 @@ struct ov08x40_mode { u32 vts_def; u32 vts_min; + /* HTS */ + u32 hts; + /* Index of Link frequency config to be used */ u32 link_freq_index; /* Default register values */ @@ -2391,6 +2392,7 @@ static const struct ov08x40_mode supported_modes[] = { .height = 2416, .vts_def = OV08X40_VTS_30FPS, .vts_min = OV08X40_VTS_30FPS, + .hts = 640, .lanes = 4, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3856x2416_regs), @@ -2403,6 +2405,7 @@ static const struct ov08x40_mode supported_modes[] = { .height = 1208, .vts_def = OV08X40_VTS_BIN_30FPS, .vts_min = OV08X40_VTS_BIN_30FPS, + .hts = 720, .lanes = 4, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1928x1208_regs), @@ -2846,9 +2849,7 @@ ov08x40_set_pad_format(struct v4l2_subdev *sd, 1, vblank_def); __v4l2_ctrl_s_ctrl(ov08x->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - ov08x->cur_mode->width; + h_blank = ov08x->cur_mode->hts; __v4l2_ctrl_modify_range(ov08x->hblank, h_blank, h_blank, 1, h_blank); } @@ -3074,8 +3075,7 @@ static int ov08x40_init_controls(struct ov08x40 *ov08x) OV08X40_VTS_MAX - mode->height, 1, vblank_def); - hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - - mode->width; + hblank = ov08x->cur_mode->hts; ov08x->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov08x40_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); @@ -3320,6 +3320,6 @@ static struct i2c_driver ov08x40_i2c_driver = { module_i2c_driver(ov08x40_i2c_driver); MODULE_AUTHOR("Jason Chen <jason.z.chen@intel.com>"); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OV08X40 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 3db3e64fa3ff..35652b362347 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1814,7 +1814,7 @@ static struct i2c_driver ov13858_i2c_driver = { module_i2c_driver(ov13858_i2c_driver); MODULE_AUTHOR("Kan, Chris <chris.kan@intel.com>"); -MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Omnivision ov13858 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 6110fb1e6bc6..dbc642c5995b 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -2,6 +2,9 @@ // Copyright (c) 2021 Intel Corporation. #include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -573,6 +576,11 @@ struct ov13b10 { struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; + + struct clk *img_clk; + struct regulator *avdd; + struct gpio_desc *reset; + /* V4L2 Controls */ struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate; @@ -1051,6 +1059,49 @@ static int ov13b10_identify_module(struct ov13b10 *ov13b) return 0; } +static int ov13b10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + gpiod_set_value_cansleep(ov13b10->reset, 1); + + if (ov13b10->avdd) + regulator_disable(ov13b10->avdd); + + clk_disable_unprepare(ov13b10->img_clk); + + return 0; +} + +static int ov13b10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + int ret; + + ret = clk_prepare_enable(ov13b10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + if (ov13b10->avdd) { + ret = regulator_enable(ov13b10->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(ov13b10->img_clk); + return ret; + } + } + + gpiod_set_value_cansleep(ov13b10->reset, 0); + /* 5ms to wait ready after XSHUTDN assert */ + usleep_range(5000, 5500); + + return 0; +} + static int ov13b10_start_streaming(struct ov13b10 *ov13b) { struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); @@ -1145,7 +1196,7 @@ err_unlock: return ret; } -static int __maybe_unused ov13b10_suspend(struct device *dev) +static int ov13b10_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13b10 *ov13b = to_ov13b10(sd); @@ -1153,26 +1204,35 @@ static int __maybe_unused ov13b10_suspend(struct device *dev) if (ov13b->streaming) ov13b10_stop_streaming(ov13b); + ov13b10_power_off(dev); + return 0; } -static int __maybe_unused ov13b10_resume(struct device *dev) +static int ov13b10_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13b10 *ov13b = to_ov13b10(sd); int ret; + ret = ov13b10_power_on(dev); + if (ret) + goto pm_fail; + if (ov13b->streaming) { ret = ov13b10_start_streaming(ov13b); if (ret) - goto error; + goto stop_streaming; } return 0; -error: +stop_streaming: ov13b10_stop_streaming(ov13b); + ov13b10_power_off(dev); +pm_fail: ov13b->streaming = false; + return ret; } @@ -1317,6 +1377,34 @@ static void ov13b10_free_controls(struct ov13b10 *ov13b) mutex_destroy(&ov13b->mutex); } +static int ov13b10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13b10 *ov13b = to_ov13b10(sd); + int ret; + + ov13b->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov13b->reset)) + return dev_err_probe(dev, PTR_ERR(ov13b->reset), + "failed to get reset gpio\n"); + + ov13b->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov13b->img_clk)) + return dev_err_probe(dev, PTR_ERR(ov13b->img_clk), + "failed to get imaging clock\n"); + + ov13b->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov13b->avdd)) { + ret = PTR_ERR(ov13b->avdd); + ov13b->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + return 0; +} + static int ov13b10_check_hwcfg(struct device *dev) { struct v4l2_fwnode_endpoint bus_cfg = { @@ -1331,6 +1419,10 @@ static int ov13b10_check_hwcfg(struct device *dev) if (!fwnode) return -ENXIO; + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EPROBE_DEFER; + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &ext_clk); if (ret) { @@ -1344,10 +1436,6 @@ static int ov13b10_check_hwcfg(struct device *dev) return -EINVAL; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) @@ -1407,13 +1495,23 @@ static int ov13b10_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); + ret = ov13b10_get_pm_resources(&client->dev); + if (ret) + return ret; + full_power = acpi_dev_state_d0(&client->dev); if (full_power) { + ov13b10_power_on(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + return ret; + } + /* Check module identity */ ret = ov13b10_identify_module(ov13b); if (ret) { dev_err(&client->dev, "failed to find sensor: %d\n", ret); - return ret; + goto error_power_off; } } @@ -1422,7 +1520,7 @@ static int ov13b10_probe(struct i2c_client *client) ret = ov13b10_init_controls(ov13b); if (ret) - return ret; + goto error_power_off; /* Initialize subdev */ ov13b->sd.internal_ops = &ov13b10_internal_ops; @@ -1462,6 +1560,9 @@ error_handler_free: ov13b10_free_controls(ov13b); dev_err(&client->dev, "%s failed:%d\n", __func__, ret); +error_power_off: + ov13b10_power_off(&client->dev); + return ret; } @@ -1477,13 +1578,13 @@ static void ov13b10_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); } -static const struct dev_pm_ops ov13b10_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend, + ov13b10_resume, NULL); #ifdef CONFIG_ACPI static const struct acpi_device_id ov13b10_acpi_ids[] = { {"OVTIDB10"}, + {"OVTI13B1"}, { /* sentinel */ } }; @@ -1493,7 +1594,7 @@ MODULE_DEVICE_TABLE(acpi, ov13b10_acpi_ids); static struct i2c_driver ov13b10_i2c_driver = { .driver = { .name = "ov13b10", - .pm = &ov13b10_pm_ops, + .pm = pm_ptr(&ov13b10_pm_ops), .acpi_match_table = ACPI_PTR(ov13b10_acpi_ids), }, .probe = ov13b10_probe, diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index ec801a81c2d0..bb6c9863a546 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1296,7 +1296,7 @@ MODULE_DEVICE_TABLE(of, ov2640_of_match); static struct i2c_driver ov2640_i2c_driver = { .driver = { .name = "ov2640", - .of_match_table = of_match_ptr(ov2640_of_match), + .of_match_table = ov2640_of_match, }, .probe = ov2640_probe, .remove = ov2640_remove, diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index d06e9fc37f77..72bab0ff8a36 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -10,61 +10,93 @@ * */ -#include <asm/unaligned.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> -#include <linux/gpio/consumer.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> -#define OV2680_XVCLK_VALUE 24000000 +#define OV2680_CHIP_ID 0x2680 -#define OV2680_CHIP_ID 0x2680 +#define OV2680_REG_STREAM_CTRL CCI_REG8(0x0100) +#define OV2680_REG_SOFT_RESET CCI_REG8(0x0103) -#define OV2680_REG_STREAM_CTRL 0x0100 -#define OV2680_REG_SOFT_RESET 0x0103 +#define OV2680_REG_CHIP_ID CCI_REG16(0x300a) +#define OV2680_REG_SC_CMMN_SUB_ID CCI_REG8(0x302a) +#define OV2680_REG_PLL_MULTIPLIER CCI_REG16(0x3081) -#define OV2680_REG_CHIP_ID_HIGH 0x300a -#define OV2680_REG_CHIP_ID_LOW 0x300b +#define OV2680_REG_EXPOSURE_PK CCI_REG24(0x3500) +#define OV2680_REG_R_MANUAL CCI_REG8(0x3503) +#define OV2680_REG_GAIN_PK CCI_REG16(0x350a) -#define OV2680_REG_R_MANUAL 0x3503 -#define OV2680_REG_GAIN_PK 0x350a -#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 -#define OV2680_REG_TIMING_HTS 0x380c -#define OV2680_REG_TIMING_VTS 0x380e -#define OV2680_REG_FORMAT1 0x3820 -#define OV2680_REG_FORMAT2 0x3821 +#define OV2680_REG_SENSOR_CTRL_0A CCI_REG8(0x370a) -#define OV2680_REG_ISP_CTRL00 0x5080 +#define OV2680_REG_HORIZONTAL_START CCI_REG16(0x3800) +#define OV2680_REG_VERTICAL_START CCI_REG16(0x3802) +#define OV2680_REG_HORIZONTAL_END CCI_REG16(0x3804) +#define OV2680_REG_VERTICAL_END CCI_REG16(0x3806) +#define OV2680_REG_HORIZONTAL_OUTPUT_SIZE CCI_REG16(0x3808) +#define OV2680_REG_VERTICAL_OUTPUT_SIZE CCI_REG16(0x380a) +#define OV2680_REG_TIMING_HTS CCI_REG16(0x380c) +#define OV2680_REG_TIMING_VTS CCI_REG16(0x380e) +#define OV2680_REG_ISP_X_WIN CCI_REG16(0x3810) +#define OV2680_REG_ISP_Y_WIN CCI_REG16(0x3812) +#define OV2680_REG_X_INC CCI_REG8(0x3814) +#define OV2680_REG_Y_INC CCI_REG8(0x3815) +#define OV2680_REG_FORMAT1 CCI_REG8(0x3820) +#define OV2680_REG_FORMAT2 CCI_REG8(0x3821) -#define OV2680_FRAME_RATE 30 +#define OV2680_REG_ISP_CTRL00 CCI_REG8(0x5080) -#define OV2680_REG_VALUE_8BIT 1 -#define OV2680_REG_VALUE_16BIT 2 -#define OV2680_REG_VALUE_24BIT 3 +#define OV2680_REG_X_WIN CCI_REG16(0x5704) +#define OV2680_REG_Y_WIN CCI_REG16(0x5706) -#define OV2680_WIDTH_MAX 1600 -#define OV2680_HEIGHT_MAX 1200 +#define OV2680_FRAME_RATE 30 -enum ov2680_mode_id { - OV2680_MODE_QUXGA_800_600, - OV2680_MODE_720P_1280_720, - OV2680_MODE_UXGA_1600_1200, - OV2680_MODE_MAX, -}; +#define OV2680_NATIVE_WIDTH 1616 +#define OV2680_NATIVE_HEIGHT 1216 +#define OV2680_NATIVE_START_LEFT 0 +#define OV2680_NATIVE_START_TOP 0 +#define OV2680_ACTIVE_WIDTH 1600 +#define OV2680_ACTIVE_HEIGHT 1200 +#define OV2680_ACTIVE_START_LEFT 8 +#define OV2680_ACTIVE_START_TOP 8 +#define OV2680_MIN_CROP_WIDTH 2 +#define OV2680_MIN_CROP_HEIGHT 2 -struct reg_value { - u16 reg_addr; - u8 val; -}; +/* Fixed pre-div of 1/2 */ +#define OV2680_PLL_PREDIV0 2 + +/* Pre-div configurable through reg 0x3080, left at its default of 0x02 : 1/2 */ +#define OV2680_PLL_PREDIV 2 + +/* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */ +#define OV2680_PIXELS_PER_LINE 1704 +#define OV2680_LINES_PER_FRAME 1294 + +/* If possible send 16 extra rows / lines to the ISP as padding */ +#define OV2680_END_MARGIN 16 + +/* Max exposure time is VTS - 8 */ +#define OV2680_INTEGRATION_TIME_MARGIN 8 + +#define OV2680_DEFAULT_WIDTH 800 +#define OV2680_DEFAULT_HEIGHT 600 + +/* For enum_frame_size() full-size + binned-/quarter-size */ +#define OV2680_FRAME_SIZES 2 static const char * const ov2680_supply_name[] = { "DOVDD", @@ -74,52 +106,74 @@ static const char * const ov2680_supply_name[] = { #define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_name) -struct ov2680_mode_info { - const char *name; - enum ov2680_mode_id id; - u32 width; - u32 height; - const struct reg_value *reg_data; - u32 reg_data_size; +enum { + OV2680_19_2_MHZ, + OV2680_24_MHZ, +}; + +static const unsigned long ov2680_xvclk_freqs[] = { + [OV2680_19_2_MHZ] = 19200000, + [OV2680_24_MHZ] = 24000000, +}; + +static const u8 ov2680_pll_multipliers[] = { + [OV2680_19_2_MHZ] = 69, + [OV2680_24_MHZ] = 55, }; struct ov2680_ctrls { struct v4l2_ctrl_handler handler; - struct { - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - }; - struct { - struct v4l2_ctrl *auto_gain; - struct v4l2_ctrl *gain; - }; - + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; struct v4l2_ctrl *hflip; struct v4l2_ctrl *vflip; struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; +}; + +struct ov2680_mode { + struct v4l2_rect crop; + struct v4l2_mbus_framefmt fmt; + struct v4l2_fract frame_interval; + bool binning; + u16 h_start; + u16 v_start; + u16 h_end; + u16 v_end; + u16 h_output_size; + u16 v_output_size; + u16 hts; + u16 vts; }; struct ov2680_dev { - struct i2c_client *i2c_client; + struct device *dev; + struct regmap *regmap; struct v4l2_subdev sd; struct media_pad pad; struct clk *xvclk; u32 xvclk_freq; + u8 pll_mult; + s64 link_freq[1]; + u64 pixel_rate; struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; - struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; struct mutex lock; /* protect members */ - bool mode_pending_changes; - bool is_enabled; bool is_streaming; struct ov2680_ctrls ctrls; - struct v4l2_mbus_framefmt fmt; - struct v4l2_fract frame_interval; + struct ov2680_mode mode; +}; - const struct ov2680_mode_info *current_mode; +static const struct v4l2_rect ov2680_default_crop = { + .left = OV2680_ACTIVE_START_LEFT, + .top = OV2680_ACTIVE_START_TOP, + .width = OV2680_ACTIVE_WIDTH, + .height = OV2680_ACTIVE_HEIGHT, }; static const char * const test_pattern_menu[] = { @@ -137,426 +191,349 @@ static const int ov2680_hv_flip_bayer_order[] = { MEDIA_BUS_FMT_SRGGB10_1X10, }; -static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = { - {0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20}, - {0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac}, - {0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04}, - {0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00}, - {0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0}, -}; +static const struct reg_sequence ov2680_global_setting[] = { + /* MIPI PHY, 0x10 -> 0x1c enable bp_c_hs_en_lat and bp_d_hs_en_lat */ + {0x3016, 0x1c}, -static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = { - {0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, - {0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05}, - {0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11}, - {0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00}, -}; + /* R MANUAL set exposure and gain to manual (hw does not do auto) */ + {0x3503, 0x03}, -static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = { - {0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06}, - {0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06}, - {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00}, - {0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0}, - {0x4008, 0x00}, {0x4837, 0x18} -}; + /* Analog control register tweaks */ + {0x3603, 0x39}, /* Reset value 0x99 */ + {0x3604, 0x24}, /* Reset value 0x74 */ + {0x3621, 0x37}, /* Reset value 0x44 */ -static const struct ov2680_mode_info ov2680_mode_init_data = { - "mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600, - ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600), -}; + /* Sensor control register tweaks */ + {0x3701, 0x64}, /* Reset value 0x61 */ + {0x3705, 0x3c}, /* Reset value 0x21 */ + {0x370c, 0x50}, /* Reset value 0x10 */ + {0x370d, 0xc0}, /* Reset value 0x00 */ + {0x3718, 0x88}, /* Reset value 0x80 */ -static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = { - {"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, - 800, 600, ov2680_setting_30fps_QUXGA_800_600, - ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)}, - {"mode_720p_1280_720", OV2680_MODE_720P_1280_720, - 1280, 720, ov2680_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)}, - {"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200, - 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200, - ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)}, -}; + /* PSRAM tweaks */ + {0x3781, 0x80}, /* Reset value 0x00 */ + {0x3784, 0x0c}, /* Reset value 0x00, based on OV2680_R1A_AM10.ovt */ + {0x3789, 0x60}, /* Reset value 0x50 */ -static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ov2680_dev, sd); -} + /* BLC CTRL00 0x01 -> 0x81 set avg_weight to 8 */ + {0x4000, 0x81}, -static struct device *ov2680_to_dev(struct ov2680_dev *sensor) -{ - return &sensor->i2c_client->dev; -} + /* Set black level compensation range to 0 - 3 (default 0 - 11) */ + {0x4008, 0x00}, + {0x4009, 0x03}, -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct ov2680_dev, - ctrls.handler)->sd; -} + /* VFIFO R2 0x00 -> 0x02 set Frame reset enable */ + {0x4602, 0x02}, -static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 val) -{ - struct i2c_client *client = sensor->i2c_client; - u8 buf[6]; - int ret; + /* MIPI ctrl CLK PREPARE MIN change from 0x26 (38) -> 0x36 (54) */ + {0x481f, 0x36}, - if (len > 4) - return -EINVAL; + /* MIPI ctrl CLK LPX P MIN change from 0x32 (50) -> 0x36 (54) */ + {0x4825, 0x36}, - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - ret = i2c_master_send(client, buf, len + 2); - if (ret != len + 2) { - dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } + /* R ISP CTRL2 0x20 -> 0x30, set sof_sel bit */ + {0x5002, 0x30}, - return 0; -} - -#define ov2680_write_reg(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v) - -#define ov2680_write_reg16(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v) - -#define ov2680_write_reg24(s, r, v) \ - __ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v) - -static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg, - unsigned int len, u32 *val) -{ - struct i2c_client *client = sensor->i2c_client; - struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; - int ret; - - if (len > 4) - return -EINVAL; - - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; + /* + * Window CONTROL 0x00 -> 0x01, enable manual window control, + * this is necessary for full size flip and mirror support. + */ + {0x5708, 0x01}, - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; + /* + * DPC CTRL0 0x14 -> 0x3e, set enable_tail, enable_3x3_cluster + * and enable_general_tail bits based OV2680_R1A_AM10.ovt. + */ + {0x5780, 0x3e}, - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) { - dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); - return -EIO; - } - - *val = get_unaligned_be32(data_buf); - - return 0; -} + /* DPC MORE CONNECTION CASE THRE 0x0c (12) -> 0x02 (2) */ + {0x5788, 0x02}, -#define ov2680_read_reg(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v) + /* DPC GAIN LIST1 0x0f (15) -> 0x08 (8) */ + {0x578e, 0x08}, -#define ov2680_read_reg16(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v) + /* DPC GAIN LIST2 0x3f (63) -> 0x0c (12) */ + {0x578f, 0x0c}, -#define ov2680_read_reg24(s, r, v) \ - __ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v) + /* DPC THRE RATIO 0x04 (4) -> 0x00 (0) */ + {0x5792, 0x00}, +}; -static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val) +static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) { - u32 readval; - int ret; - - ret = ov2680_read_reg(sensor, reg, &readval); - if (ret < 0) - return ret; - - readval &= ~mask; - val &= mask; - val |= readval; - - return ov2680_write_reg(sensor, reg, val); + return container_of(sd, struct ov2680_dev, sd); } -static int ov2680_load_regs(struct ov2680_dev *sensor, - const struct ov2680_mode_info *mode) +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) { - const struct reg_value *regs = mode->reg_data; - unsigned int i; - int ret = 0; - u16 reg_addr; - u8 val; - - for (i = 0; i < mode->reg_data_size; ++i, ++regs) { - reg_addr = regs->reg_addr; - val = regs->val; - - ret = ov2680_write_reg(sensor, reg_addr, val); - if (ret) - break; - } - - return ret; + return &container_of(ctrl->handler, struct ov2680_dev, + ctrls.handler)->sd; } static void ov2680_power_up(struct ov2680_dev *sensor) { - if (!sensor->reset_gpio) + if (!sensor->pwdn_gpio) return; - gpiod_set_value(sensor->reset_gpio, 0); + gpiod_set_value(sensor->pwdn_gpio, 0); usleep_range(5000, 10000); } static void ov2680_power_down(struct ov2680_dev *sensor) { - if (!sensor->reset_gpio) + if (!sensor->pwdn_gpio) return; - gpiod_set_value(sensor->reset_gpio, 1); + gpiod_set_value(sensor->pwdn_gpio, 1); usleep_range(5000, 10000); } -static int ov2680_bayer_order(struct ov2680_dev *sensor) +static void ov2680_set_bayer_order(struct ov2680_dev *sensor, + struct v4l2_mbus_framefmt *fmt) { - u32 format1; - u32 format2; - u32 hv_flip; - int ret; - - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1); - if (ret < 0) - return ret; + int hv_flip = 0; - ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2); - if (ret < 0) - return ret; + if (sensor->ctrls.vflip && sensor->ctrls.vflip->val) + hv_flip += 1; - hv_flip = (format2 & BIT(2) << 1) | (format1 & BIT(2)); + if (sensor->ctrls.hflip && sensor->ctrls.hflip->val) + hv_flip += 2; - sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip]; - - return 0; + fmt->code = ov2680_hv_flip_bayer_order[hv_flip]; } -static int ov2680_vflip_enable(struct ov2680_dev *sensor) +static struct v4l2_mbus_framefmt * +__ov2680_get_pad_format(struct ov2680_dev *sensor, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) { - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2)); - if (ret < 0) - return ret; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&sensor->sd, state, pad); - return ov2680_bayer_order(sensor); + return &sensor->mode.fmt; } -static int ov2680_vflip_disable(struct ov2680_dev *sensor) +static struct v4l2_rect * +__ov2680_get_pad_crop(struct ov2680_dev *sensor, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) { - int ret; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&sensor->sd, state, pad); - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); + return &sensor->mode.crop; } -static int ov2680_hflip_enable(struct ov2680_dev *sensor) +static void ov2680_fill_format(struct ov2680_dev *sensor, + struct v4l2_mbus_framefmt *fmt, + unsigned int width, unsigned int height) { - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2)); - if (ret < 0) - return ret; - - return ov2680_bayer_order(sensor); + memset(fmt, 0, sizeof(*fmt)); + fmt->width = width; + fmt->height = height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + ov2680_set_bayer_order(sensor, fmt); } -static int ov2680_hflip_disable(struct ov2680_dev *sensor) +static void ov2680_calc_mode(struct ov2680_dev *sensor) { - int ret; - - ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0)); - if (ret < 0) - return ret; + int width = sensor->mode.fmt.width; + int height = sensor->mode.fmt.height; + int orig_width = width; + int orig_height = height; + + if (width <= (sensor->mode.crop.width / 2) && + height <= (sensor->mode.crop.height / 2)) { + sensor->mode.binning = true; + width *= 2; + height *= 2; + } else { + sensor->mode.binning = false; + } - return ov2680_bayer_order(sensor); + sensor->mode.h_start = (sensor->mode.crop.left + + (sensor->mode.crop.width - width) / 2) & ~1; + sensor->mode.v_start = (sensor->mode.crop.top + + (sensor->mode.crop.height - height) / 2) & ~1; + sensor->mode.h_end = + min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1, + OV2680_NATIVE_WIDTH - 1); + sensor->mode.v_end = + min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1, + OV2680_NATIVE_HEIGHT - 1); + sensor->mode.h_output_size = orig_width; + sensor->mode.v_output_size = orig_height; + sensor->mode.hts = OV2680_PIXELS_PER_LINE; + sensor->mode.vts = OV2680_LINES_PER_FRAME; } -static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) +static int ov2680_set_mode(struct ov2680_dev *sensor) { - int ret; - - if (!value) - return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0); + u8 sensor_ctrl_0a, inc, fmt1, fmt2; + int ret = 0; - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1); - if (ret < 0) - return ret; + if (sensor->mode.binning) { + sensor_ctrl_0a = 0x23; + inc = 0x31; + fmt1 = 0xc2; + fmt2 = 0x01; + } else { + sensor_ctrl_0a = 0x21; + inc = 0x11; + fmt1 = 0xc0; + fmt2 = 0x00; + } - ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); - if (ret < 0) - return ret; + cci_write(sensor->regmap, OV2680_REG_SENSOR_CTRL_0A, + sensor_ctrl_0a, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_START, + sensor->mode.h_start, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_START, + sensor->mode.v_start, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_END, + sensor->mode.h_end, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_END, + sensor->mode.v_end, &ret); + cci_write(sensor->regmap, OV2680_REG_HORIZONTAL_OUTPUT_SIZE, + sensor->mode.h_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE, + sensor->mode.v_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_TIMING_HTS, + sensor->mode.hts, &ret); + cci_write(sensor->regmap, OV2680_REG_TIMING_VTS, + sensor->mode.vts, &ret); + cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret); + cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret); + cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret); + cci_write(sensor->regmap, OV2680_REG_Y_INC, inc, &ret); + cci_write(sensor->regmap, OV2680_REG_X_WIN, + sensor->mode.h_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_Y_WIN, + sensor->mode.v_output_size, &ret); + cci_write(sensor->regmap, OV2680_REG_FORMAT1, fmt1, &ret); + cci_write(sensor->regmap, OV2680_REG_FORMAT2, fmt2, &ret); - return 0; + return ret; } -static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain) +static int ov2680_set_vflip(struct ov2680_dev *sensor, s32 val) { - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 gain; int ret; - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1), - auto_gain ? 0 : BIT(1)); + if (sensor->is_streaming) + return -EBUSY; + + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT1, + BIT(2), val ? BIT(2) : 0, NULL); if (ret < 0) return ret; - if (auto_gain || !ctrls->gain->is_new) - return 0; - - gain = ctrls->gain->val; - - ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain); - + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); return 0; } -static int ov2680_gain_get(struct ov2680_dev *sensor) +static int ov2680_set_hflip(struct ov2680_dev *sensor, s32 val) { - u32 gain; int ret; - ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain); - if (ret) - return ret; - - return gain; -} - -static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp) -{ - struct ov2680_ctrls *ctrls = &sensor->ctrls; - u32 exp; - int ret; + if (sensor->is_streaming) + return -EBUSY; - ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0), - auto_exp ? 0 : BIT(0)); + ret = cci_update_bits(sensor->regmap, OV2680_REG_FORMAT2, + BIT(2), val ? BIT(2) : 0, NULL); if (ret < 0) return ret; - if (auto_exp || !ctrls->exposure->is_new) - return 0; - - exp = (u32)ctrls->exposure->val; - exp <<= 4; - - return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp); + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); + return 0; } -static int ov2680_exposure_get(struct ov2680_dev *sensor) +static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) { - int ret; - u32 exp; + int ret = 0; - ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp); - if (ret) - return ret; + if (!value) + return cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + BIT(7), 0, NULL); - return exp >> 4; + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + 0x03, value - 1, &ret); + cci_update_bits(sensor->regmap, OV2680_REG_ISP_CTRL00, + BIT(7), BIT(7), &ret); + + return ret; } -static int ov2680_stream_enable(struct ov2680_dev *sensor) +static int ov2680_gain_set(struct ov2680_dev *sensor, u32 gain) { - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1); + return cci_write(sensor->regmap, OV2680_REG_GAIN_PK, gain, NULL); } -static int ov2680_stream_disable(struct ov2680_dev *sensor) +static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp) { - return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0); + return cci_write(sensor->regmap, OV2680_REG_EXPOSURE_PK, exp << 4, + NULL); } -static int ov2680_mode_set(struct ov2680_dev *sensor) +static int ov2680_stream_enable(struct ov2680_dev *sensor) { - struct ov2680_ctrls *ctrls = &sensor->ctrls; int ret; - ret = ov2680_gain_set(sensor, false); + ret = cci_write(sensor->regmap, OV2680_REG_PLL_MULTIPLIER, + sensor->pll_mult, NULL); if (ret < 0) return ret; - ret = ov2680_exposure_set(sensor, false); + ret = regmap_multi_reg_write(sensor->regmap, + ov2680_global_setting, + ARRAY_SIZE(ov2680_global_setting)); if (ret < 0) return ret; - ret = ov2680_load_regs(sensor, sensor->current_mode); + ret = ov2680_set_mode(sensor); if (ret < 0) return ret; - if (ctrls->auto_gain->val) { - ret = ov2680_gain_set(sensor, true); - if (ret < 0) - return ret; - } - - if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) { - ret = ov2680_exposure_set(sensor, true); - if (ret < 0) - return ret; - } - - sensor->mode_pending_changes = false; + /* Restore value of all ctrls */ + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret < 0) + return ret; - return 0; + return cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 1, NULL); } -static int ov2680_mode_restore(struct ov2680_dev *sensor) +static int ov2680_stream_disable(struct ov2680_dev *sensor) { - int ret; - - ret = ov2680_load_regs(sensor, &ov2680_mode_init_data); - if (ret < 0) - return ret; - - return ov2680_mode_set(sensor); + return cci_write(sensor->regmap, OV2680_REG_STREAM_CTRL, 0, NULL); } static int ov2680_power_off(struct ov2680_dev *sensor) { - if (!sensor->is_enabled) - return 0; - clk_disable_unprepare(sensor->xvclk); ov2680_power_down(sensor); regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); - sensor->is_enabled = false; - return 0; } static int ov2680_power_on(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); int ret; - if (sensor->is_enabled) - return 0; - ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, sensor->supplies); if (ret < 0) { - dev_err(dev, "failed to enable regulators: %d\n", ret); + dev_err(sensor->dev, "failed to enable regulators: %d\n", ret); return ret; } - if (!sensor->reset_gpio) { - ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01); + if (!sensor->pwdn_gpio) { + ret = cci_write(sensor->regmap, OV2680_REG_SOFT_RESET, 0x01, + NULL); if (ret != 0) { - dev_err(dev, "sensor soft reset failed\n"); - return ret; + dev_err(sensor->dev, "sensor soft reset failed\n"); + goto err_disable_regulators; } usleep_range(1000, 2000); } else { @@ -566,40 +543,12 @@ static int ov2680_power_on(struct ov2680_dev *sensor) ret = clk_prepare_enable(sensor->xvclk); if (ret < 0) - return ret; - - sensor->is_enabled = true; - - /* Set clock lane into LP-11 state */ - ov2680_stream_enable(sensor); - usleep_range(1000, 2000); - ov2680_stream_disable(sensor); + goto err_disable_regulators; return 0; -} - -static int ov2680_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov2680_dev *sensor = to_ov2680_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - if (on) - ret = ov2680_power_on(sensor); - else - ret = ov2680_power_off(sensor); - - mutex_unlock(&sensor->lock); - - if (on && ret == 0) { - ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret < 0) - return ret; - - ret = ov2680_mode_restore(sensor); - } +err_disable_regulators: + regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); return ret; } @@ -609,7 +558,7 @@ static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, struct ov2680_dev *sensor = to_ov2680_dev(sd); mutex_lock(&sensor->lock); - fi->interval = sensor->frame_interval; + fi->interval = sensor->mode.frame_interval; mutex_unlock(&sensor->lock); return 0; @@ -625,16 +574,20 @@ static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) if (sensor->is_streaming == !!enable) goto unlock; - if (enable && sensor->mode_pending_changes) { - ret = ov2680_mode_set(sensor); + if (enable) { + ret = pm_runtime_resume_and_get(sensor->sd.dev); if (ret < 0) goto unlock; - } - if (enable) ret = ov2680_stream_enable(sensor); - else + if (ret < 0) { + pm_runtime_put(sensor->sd.dev); + goto unlock; + } + } else { ret = ov2680_stream_disable(sensor); + pm_runtime_put(sensor->sd.dev); + } sensor->is_streaming = !!enable; @@ -650,10 +603,10 @@ static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, { struct ov2680_dev *sensor = to_ov2680_dev(sd); - if (code->pad != 0 || code->index != 0) + if (code->index != 0) return -EINVAL; - code->code = sensor->fmt.code; + code->code = sensor->mode.fmt.code; return 0; } @@ -663,31 +616,16 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = NULL; - int ret = 0; + struct v4l2_mbus_framefmt *fmt; - if (format->pad != 0) - return -EINVAL; + fmt = __ov2680_get_pad_format(sensor, sd_state, format->pad, + format->which); mutex_lock(&sensor->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - fmt = v4l2_subdev_get_try_format(&sensor->sd, sd_state, - format->pad); -#else - ret = -EINVAL; -#endif - } else { - fmt = &sensor->fmt; - } - - if (fmt) - format->format = *fmt; - + format->format = *fmt; mutex_unlock(&sensor->lock); - return ret; + return 0; } static int ov2680_set_fmt(struct v4l2_subdev *sd, @@ -695,15 +633,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct v4l2_mbus_framefmt *fmt = &format->format; -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API struct v4l2_mbus_framefmt *try_fmt; -#endif - const struct ov2680_mode_info *mode; + const struct v4l2_rect *crop; + unsigned int width, height; int ret = 0; - if (format->pad != 0) - return -EINVAL; + crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad, + format->which); + + /* Limit set_fmt max size to crop width / height */ + width = clamp_val(ALIGN(format->format.width, 2), + OV2680_MIN_CROP_WIDTH, crop->width); + height = clamp_val(ALIGN(format->format.height, 2), + OV2680_MIN_CROP_HEIGHT, crop->height); + + ov2680_fill_format(sensor, &format->format, width, height); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); + *try_fmt = format->format; + return 0; + } mutex_lock(&sensor->lock); @@ -712,112 +662,168 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, goto unlock; } - mode = v4l2_find_nearest_size(ov2680_mode_data, - ARRAY_SIZE(ov2680_mode_data), width, - height, fmt->width, fmt->height); - if (!mode) { - ret = -EINVAL; - goto unlock; - } + sensor->mode.fmt = format->format; + ov2680_calc_mode(sensor); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API - try_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0); - format->format = *try_fmt; -#endif - goto unlock; +unlock: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&sensor->lock); + sel->r = *__ov2680_get_pad_crop(sensor, state, sel->pad, + sel->which); + mutex_unlock(&sensor->lock); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV2680_NATIVE_WIDTH; + sel->r.height = OV2680_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = ov2680_default_crop; + break; + default: + return -EINVAL; } - fmt->width = mode->width; - fmt->height = mode->height; - fmt->code = sensor->fmt.code; - fmt->colorspace = sensor->fmt.colorspace; + return 0; +} - sensor->current_mode = mode; - sensor->fmt = format->format; - sensor->mode_pending_changes = true; +static int ov2680_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect rect; -unlock: + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * Clamp the boundaries of the crop rectangle to the size of the sensor + * pixel array. Align to multiples of 2 to ensure Bayer pattern isn't + * disrupted. + */ + rect.left = clamp_val(ALIGN(sel->r.left, 2), + OV2680_NATIVE_START_LEFT, OV2680_NATIVE_WIDTH); + rect.top = clamp_val(ALIGN(sel->r.top, 2), + OV2680_NATIVE_START_TOP, OV2680_NATIVE_HEIGHT); + rect.width = clamp_val(ALIGN(sel->r.width, 2), + OV2680_MIN_CROP_WIDTH, OV2680_NATIVE_WIDTH); + rect.height = clamp_val(ALIGN(sel->r.height, 2), + OV2680_MIN_CROP_HEIGHT, OV2680_NATIVE_HEIGHT); + + /* Make sure the crop rectangle isn't outside the bounds of the array */ + rect.width = min_t(unsigned int, rect.width, + OV2680_NATIVE_WIDTH - rect.left); + rect.height = min_t(unsigned int, rect.height, + OV2680_NATIVE_HEIGHT - rect.top); + + crop = __ov2680_get_pad_crop(sensor, state, sel->pad, sel->which); + + mutex_lock(&sensor->lock); + if (rect.width != crop->width || rect.height != crop->height) { + /* + * Reset the output image size if the crop rectangle size has + * been modified. + */ + format = __ov2680_get_pad_format(sensor, state, sel->pad, + sel->which); + format->width = rect.width; + format->height = rect.height; + } + + *crop = rect; mutex_unlock(&sensor->lock); - return ret; + sel->r = rect; + + return 0; } static int ov2680_init_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - struct v4l2_subdev_format fmt = { - .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY - : V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .width = 800, - .height = 600, - } - }; + struct ov2680_dev *sensor = to_ov2680_dev(sd); - return ov2680_set_fmt(sd, sd_state, &fmt); + sd_state->pads[0].try_crop = ov2680_default_crop; + + ov2680_fill_format(sensor, &sd_state->pads[0].try_fmt, + OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); + return 0; } static int ov2680_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - int index = fse->index; + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_rect *crop; + + if (fse->index >= OV2680_FRAME_SIZES) + return -EINVAL; - if (index >= OV2680_MODE_MAX || index < 0) + crop = __ov2680_get_pad_crop(sensor, sd_state, fse->pad, fse->which); + if (!crop) return -EINVAL; - fse->min_width = ov2680_mode_data[index].width; - fse->min_height = ov2680_mode_data[index].height; - fse->max_width = ov2680_mode_data[index].width; - fse->max_height = ov2680_mode_data[index].height; + fse->min_width = crop->width / (fse->index + 1); + fse->min_height = crop->height / (fse->index + 1); + fse->max_width = fse->min_width; + fse->max_height = fse->min_height; return 0; } -static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval_enum *fie) +static bool ov2680_valid_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) { - struct v4l2_fract tpf; + struct v4l2_subdev_frame_size_enum fse = { + .pad = fie->pad, + .which = fie->which, + }; + int i; - if (fie->index >= OV2680_MODE_MAX || fie->width > OV2680_WIDTH_MAX || - fie->height > OV2680_HEIGHT_MAX || - fie->which > V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; + for (i = 0; i < OV2680_FRAME_SIZES; i++) { + fse.index = i; - tpf.denominator = OV2680_FRAME_RATE; - tpf.numerator = 1; + if (ov2680_enum_frame_size(sd, sd_state, &fse)) + return false; - fie->interval = tpf; + if (fie->width == fse.min_width && + fie->height == fse.min_height) + return true; + } - return 0; + return false; } -static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) { - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; - int val; - if (!sensor->is_enabled) - return 0; + /* Only 1 framerate */ + if (fie->index || !ov2680_valid_frame_size(sd, sd_state, fie)) + return -EINVAL; - switch (ctrl->id) { - case V4L2_CID_GAIN: - val = ov2680_gain_get(sensor); - if (val < 0) - return val; - ctrls->gain->val = val; - break; - case V4L2_CID_EXPOSURE: - val = ov2680_exposure_get(sensor); - if (val < 0) - return val; - ctrls->exposure->val = val; - break; - } + fie->interval = sensor->mode.frame_interval; return 0; } @@ -826,52 +832,43 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov2680_dev *sensor = to_ov2680_dev(sd); - struct ov2680_ctrls *ctrls = &sensor->ctrls; + int ret; - if (!sensor->is_enabled) + /* Only apply changes to the controls if the device is powered up */ + if (!pm_runtime_get_if_in_use(sensor->sd.dev)) { + ov2680_set_bayer_order(sensor, &sensor->mode.fmt); return 0; + } switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - return ov2680_gain_set(sensor, !!ctrl->val); - case V4L2_CID_GAIN: - return ov2680_gain_set(sensor, !!ctrls->auto_gain->val); - case V4L2_CID_EXPOSURE_AUTO: - return ov2680_exposure_set(sensor, !!ctrl->val); + case V4L2_CID_ANALOGUE_GAIN: + ret = ov2680_gain_set(sensor, ctrl->val); + break; case V4L2_CID_EXPOSURE: - return ov2680_exposure_set(sensor, !!ctrls->auto_exp->val); + ret = ov2680_exposure_set(sensor, ctrl->val); + break; case V4L2_CID_VFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_vflip_enable(sensor); - else - return ov2680_vflip_disable(sensor); + ret = ov2680_set_vflip(sensor, ctrl->val); + break; case V4L2_CID_HFLIP: - if (sensor->is_streaming) - return -EBUSY; - if (ctrl->val) - return ov2680_hflip_enable(sensor); - else - return ov2680_hflip_disable(sensor); + ret = ov2680_set_hflip(sensor, ctrl->val); + break; case V4L2_CID_TEST_PATTERN: - return ov2680_test_pattern_set(sensor, ctrl->val); + ret = ov2680_test_pattern_set(sensor, ctrl->val); + break; default: + ret = -EINVAL; break; } - return -EINVAL; + pm_runtime_put(sensor->sd.dev); + return ret; } static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { - .g_volatile_ctrl = ov2680_g_volatile_ctrl, .s_ctrl = ov2680_s_ctrl, }; -static const struct v4l2_subdev_core_ops ov2680_core_ops = { - .s_power = ov2680_s_power, -}; - static const struct v4l2_subdev_video_ops ov2680_video_ops = { .g_frame_interval = ov2680_s_g_frame_interval, .s_frame_interval = ov2680_s_g_frame_interval, @@ -881,54 +878,45 @@ static const struct v4l2_subdev_video_ops ov2680_video_ops = { static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { .init_cfg = ov2680_init_cfg, .enum_mbus_code = ov2680_enum_mbus_code, - .get_fmt = ov2680_get_fmt, - .set_fmt = ov2680_set_fmt, .enum_frame_size = ov2680_enum_frame_size, .enum_frame_interval = ov2680_enum_frame_interval, + .get_fmt = ov2680_get_fmt, + .set_fmt = ov2680_set_fmt, + .get_selection = ov2680_get_selection, + .set_selection = ov2680_set_selection, }; static const struct v4l2_subdev_ops ov2680_subdev_ops = { - .core = &ov2680_core_ops, .video = &ov2680_video_ops, .pad = &ov2680_pad_ops, }; static int ov2680_mode_init(struct ov2680_dev *sensor) { - const struct ov2680_mode_info *init_mode; - /* set initial mode */ - sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; - sensor->fmt.width = 800; - sensor->fmt.height = 600; - sensor->fmt.field = V4L2_FIELD_NONE; - sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB; - - sensor->frame_interval.denominator = OV2680_FRAME_RATE; - sensor->frame_interval.numerator = 1; + sensor->mode.crop = ov2680_default_crop; + ov2680_fill_format(sensor, &sensor->mode.fmt, + OV2680_DEFAULT_WIDTH, OV2680_DEFAULT_HEIGHT); + ov2680_calc_mode(sensor); - init_mode = &ov2680_mode_init_data; - - sensor->current_mode = init_mode; - - sensor->mode_pending_changes = true; + sensor->mode.frame_interval.denominator = OV2680_FRAME_RATE; + sensor->mode.frame_interval.numerator = 1; return 0; } static int ov2680_v4l2_register(struct ov2680_dev *sensor) { + struct i2c_client *client = to_i2c_client(sensor->dev); const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; struct ov2680_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN; int ret = 0; - v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client, - &ov2680_subdev_ops); + v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops); -#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; -#endif sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -936,7 +924,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) if (ret < 0) return ret; - v4l2_ctrl_handler_init(hdl, 7); + v4l2_ctrl_handler_init(hdl, 5); hdl->lock = &sensor->lock; @@ -948,30 +936,26 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, - V4L2_EXPOSURE_AUTO); - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 32767, 1, 0); + 0, exp_max, 1, exp_max); - ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, - 0, 1, 1, 1); - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, + 0, 1023, 1, 250); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ, + 0, 0, sensor->link_freq); + ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, + 0, sensor->pixel_rate, + 1, sensor->pixel_rate); if (hdl->error) { ret = hdl->error; goto cleanup_entity; } - ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - - v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); - v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = hdl; @@ -995,61 +979,153 @@ static int ov2680_get_regulators(struct ov2680_dev *sensor) for (i = 0; i < OV2680_NUM_SUPPLIES; i++) sensor->supplies[i].supply = ov2680_supply_name[i]; - return devm_regulator_bulk_get(&sensor->i2c_client->dev, - OV2680_NUM_SUPPLIES, - sensor->supplies); + return devm_regulator_bulk_get(sensor->dev, + OV2680_NUM_SUPPLIES, sensor->supplies); } static int ov2680_check_id(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); - u32 chip_id; - int ret; - - ov2680_power_on(sensor); + u64 chip_id, rev; + int ret = 0; - ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id); + cci_read(sensor->regmap, OV2680_REG_CHIP_ID, &chip_id, &ret); + cci_read(sensor->regmap, OV2680_REG_SC_CMMN_SUB_ID, &rev, &ret); if (ret < 0) { - dev_err(dev, "failed to read chip id high\n"); - return -ENODEV; + dev_err(sensor->dev, "failed to read chip id\n"); + return ret; } if (chip_id != OV2680_CHIP_ID) { - dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n", + dev_err(sensor->dev, "chip id: 0x%04llx does not match expected 0x%04x\n", chip_id, OV2680_CHIP_ID); return -ENODEV; } + dev_info(sensor->dev, "sensor_revision id = 0x%llx, rev= %lld\n", + chip_id, rev & 0x0f); + return 0; } static int ov2680_parse_dt(struct ov2680_dev *sensor) { - struct device *dev = ov2680_to_dev(sensor); - int ret; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = sensor->dev; + struct fwnode_handle *ep_fwnode; + struct gpio_desc *gpio; + unsigned int rate = 0; + int i, ret; + + /* + * Sometimes the fwnode graph is initialized by the bridge driver. + * Bridge drivers doing this may also add GPIO mappings, wait for this. + */ + ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!ep_fwnode) + return dev_err_probe(dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg); + fwnode_handle_put(ep_fwnode); + if (ret) + return ret; - sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_HIGH); - ret = PTR_ERR_OR_ZERO(sensor->reset_gpio); + /* + * The pin we want is named XSHUTDN in the datasheet. Linux sensor + * drivers have standardized on using "powerdown" as con-id name + * for powerdown or shutdown pins. Older DTB files use "reset", + * so fallback to that if there is no "powerdown" pin. + */ + gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (!gpio) + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + + ret = PTR_ERR_OR_ZERO(gpio); if (ret < 0) { dev_dbg(dev, "error while getting reset gpio: %d\n", ret); - return ret; + goto out_free_bus_cfg; } - sensor->xvclk = devm_clk_get(dev, "xvclk"); + sensor->pwdn_gpio = gpio; + + sensor->xvclk = devm_clk_get_optional(dev, "xvclk"); if (IS_ERR(sensor->xvclk)) { - dev_err(dev, "xvclk clock missing or invalid\n"); - return PTR_ERR(sensor->xvclk); + ret = dev_err_probe(dev, PTR_ERR(sensor->xvclk), + "xvclk clock missing or invalid\n"); + goto out_free_bus_cfg; } - sensor->xvclk_freq = clk_get_rate(sensor->xvclk); - if (sensor->xvclk_freq != OV2680_XVCLK_VALUE) { - dev_err(dev, "wrong xvclk frequency %d HZ, expected: %d Hz\n", - sensor->xvclk_freq, OV2680_XVCLK_VALUE); - return -EINVAL; + /* + * We could have either a 24MHz or 19.2MHz clock rate from either DT or + * ACPI... but we also need to support the weird IPU3 case which will + * have an external clock AND a clock-frequency property. Check for the + * clock-frequency property and if found, set that rate if we managed + * to acquire a clock. This should cover the ACPI case. If the system + * uses devicetree then the configured rate should already be set, so + * we can just read it. + */ + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &rate); + if (ret && !sensor->xvclk) { + dev_err_probe(dev, ret, "invalid clock config\n"); + goto out_free_bus_cfg; } - return 0; + if (!ret && sensor->xvclk) { + ret = clk_set_rate(sensor->xvclk, rate); + if (ret) { + dev_err_probe(dev, ret, "failed to set clock rate\n"); + goto out_free_bus_cfg; + } + } + + sensor->xvclk_freq = rate ?: clk_get_rate(sensor->xvclk); + + for (i = 0; i < ARRAY_SIZE(ov2680_xvclk_freqs); i++) { + if (sensor->xvclk_freq == ov2680_xvclk_freqs[i]) + break; + } + + if (i == ARRAY_SIZE(ov2680_xvclk_freqs)) { + ret = dev_err_probe(dev, -EINVAL, + "unsupported xvclk frequency %d Hz\n", + sensor->xvclk_freq); + goto out_free_bus_cfg; + } + + sensor->pll_mult = ov2680_pll_multipliers[i]; + + sensor->link_freq[0] = sensor->xvclk_freq / OV2680_PLL_PREDIV0 / + OV2680_PLL_PREDIV * sensor->pll_mult; + + /* CSI-2 is double data rate, bus-format is 10 bpp */ + sensor->pixel_rate = sensor->link_freq[0] * 2; + do_div(sensor->pixel_rate, 10); + + /* Verify bus cfg */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) { + ret = dev_err_probe(dev, -EINVAL, + "only a 1-lane CSI2 config is supported"); + goto out_free_bus_cfg; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == sensor->link_freq[0]) + break; + + if (bus_cfg.nr_of_link_frequencies == 0 || + bus_cfg.nr_of_link_frequencies == i) { + ret = dev_err_probe(dev, -EINVAL, + "supported link freq %lld not found\n", + sensor->link_freq[0]); + goto out_free_bus_cfg; + } + +out_free_bus_cfg: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; } static int ov2680_probe(struct i2c_client *client) @@ -1062,11 +1138,15 @@ static int ov2680_probe(struct i2c_client *client) if (!sensor) return -ENOMEM; - sensor->i2c_client = client; + sensor->dev = &client->dev; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return PTR_ERR(sensor->regmap); ret = ov2680_parse_dt(sensor); if (ret < 0) - return -EINVAL; + return ret; ret = ov2680_mode_init(sensor); if (ret < 0) @@ -1080,18 +1160,37 @@ static int ov2680_probe(struct i2c_client *client) mutex_init(&sensor->lock); - ret = ov2680_check_id(sensor); + /* + * Power up and verify the chip now, so that if runtime pm is + * disabled the chip is left on and streaming will work. + */ + ret = ov2680_power_on(sensor); if (ret < 0) goto lock_destroy; + ret = ov2680_check_id(sensor); + if (ret < 0) + goto err_powerdown; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + ret = ov2680_v4l2_register(sensor); if (ret < 0) - goto lock_destroy; + goto err_pm_runtime; - dev_info(dev, "ov2680 init correctly\n"); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return 0; +err_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_put_noidle(&client->dev); +err_powerdown: + ov2680_power_off(sensor); lock_destroy: dev_err(dev, "ov2680 init fail: %d\n", ret); mutex_destroy(&sensor->lock); @@ -1108,9 +1207,18 @@ static void ov2680_remove(struct i2c_client *client) mutex_destroy(&sensor->lock); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls.handler); + + /* + * Disable runtime PM. In case runtime PM is disabled in the kernel, + * make sure to turn power off manually. + */ + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov2680_power_off(sensor); + pm_runtime_set_suspended(&client->dev); } -static int __maybe_unused ov2680_suspend(struct device *dev) +static int ov2680_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); @@ -1118,15 +1226,19 @@ static int __maybe_unused ov2680_suspend(struct device *dev) if (sensor->is_streaming) ov2680_stream_disable(sensor); - return 0; + return ov2680_power_off(sensor); } -static int __maybe_unused ov2680_resume(struct device *dev) +static int ov2680_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); int ret; + ret = ov2680_power_on(sensor); + if (ret < 0) + goto stream_disable; + if (sensor->is_streaming) { ret = ov2680_stream_enable(sensor); if (ret < 0) @@ -1142,9 +1254,8 @@ stream_disable: return ret; } -static const struct dev_pm_ops ov2680_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ov2680_pm_ops, ov2680_suspend, ov2680_resume, + NULL); static const struct of_device_id ov2680_dt_ids[] = { { .compatible = "ovti,ov2680" }, @@ -1152,11 +1263,18 @@ static const struct of_device_id ov2680_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, ov2680_dt_ids); +static const struct acpi_device_id ov2680_acpi_ids[] = { + { "OVTI2680" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, ov2680_acpi_ids); + static struct i2c_driver ov2680_i2c_driver = { .driver = { .name = "ov2680", - .pm = &ov2680_pm_ops, - .of_match_table = of_match_ptr(ov2680_dt_ids), + .pm = pm_sleep_ptr(&ov2680_pm_ops), + .of_match_table = ov2680_dt_ids, + .acpi_match_table = ov2680_acpi_ids, }, .probe = ov2680_probe, .remove = ov2680_remove, diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index 158d934733c3..41d4f85470fd 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1223,7 +1223,7 @@ static struct i2c_driver ov2740_i2c_driver = { module_i2c_driver(ov2740_i2c_driver); MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); MODULE_DESCRIPTION("OmniVision OV2740 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 36b509714c8c..5fe85aa2d2ec 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -13,8 +13,8 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -568,9 +568,7 @@ static const struct reg_value ov5640_init_setting[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, - {0x501f, 0x00, 0, 0}, {0x4407, 0x04, 0, 0}, - {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x501f, 0x00, 0, 0}, {0x440e, 0x00, 0, 0}, {0x4837, 0x0a, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, @@ -634,7 +632,8 @@ static const struct reg_value ov5640_setting_low_res[] = { {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; static const struct reg_value ov5640_setting_720P_1280_720[] = { @@ -2453,16 +2452,13 @@ static void ov5640_power(struct ov5640_dev *sensor, bool enable) static void ov5640_powerup_sequence(struct ov5640_dev *sensor) { if (sensor->pwdn_gpio) { - gpiod_set_value_cansleep(sensor->reset_gpio, 0); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); /* camera power cycle */ ov5640_power(sensor, false); - usleep_range(5000, 10000); + usleep_range(5000, 10000); /* t2 */ ov5640_power(sensor, true); - usleep_range(5000, 10000); - - gpiod_set_value_cansleep(sensor->reset_gpio, 1); - usleep_range(1000, 2000); + usleep_range(1000, 2000); /* t3 */ gpiod_set_value_cansleep(sensor->reset_gpio, 0); } else { @@ -2470,7 +2466,7 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor) ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, OV5640_REG_SYS_CTRL0_SW_RST); } - usleep_range(20000, 25000); + usleep_range(20000, 25000); /* t4 */ /* * software standby: allows registers programming; @@ -2543,9 +2539,9 @@ static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on) * "ov5640_set_stream_mipi()") * [4] = 0 : Power up MIPI HS Tx * [3] = 0 : Power up MIPI LS Rx - * [2] = 0 : MIPI interface disabled + * [2] = 1 : MIPI interface enabled */ - ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x40); + ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x44); if (ret) return ret; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index d722348b938b..29e773a997dd 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2860,7 +2860,7 @@ static struct i2c_driver ov5670_i2c_driver = { module_i2c_driver(ov5670_i2c_driver); -MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi"); MODULE_AUTHOR("Yang, Hyungwoo"); MODULE_DESCRIPTION("Omnivision ov5670 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 700c4b69846f..d5a2a5f82312 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -1442,6 +1442,6 @@ static struct i2c_driver ov5675_i2c_driver = { module_i2c_driver(ov5675_i2c_driver); -MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>"); +MODULE_AUTHOR("Shawn Tu"); MODULE_DESCRIPTION("OmniVision OV5675 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 7f9212cce239..488ee6d9d301 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -12,7 +12,6 @@ * Jake Day */ -#include <asm/unaligned.h> #include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> @@ -23,36 +22,32 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> + +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> -#define OV5693_REG_8BIT(n) ((1 << 16) | (n)) -#define OV5693_REG_16BIT(n) ((2 << 16) | (n)) -#define OV5693_REG_24BIT(n) ((3 << 16) | (n)) -#define OV5693_REG_SIZE_SHIFT 16 -#define OV5693_REG_ADDR_MASK 0xffff - /* System Control */ -#define OV5693_SW_RESET_REG OV5693_REG_8BIT(0x0103) -#define OV5693_SW_STREAM_REG OV5693_REG_8BIT(0x0100) +#define OV5693_SW_RESET_REG CCI_REG8(0x0103) +#define OV5693_SW_STREAM_REG CCI_REG8(0x0100) #define OV5693_START_STREAMING 0x01 #define OV5693_STOP_STREAMING 0x00 #define OV5693_SW_RESET 0x01 -#define OV5693_REG_CHIP_ID OV5693_REG_16BIT(0x300a) +#define OV5693_REG_CHIP_ID CCI_REG16(0x300a) /* Yes, this is right. The datasheet for the OV5693 gives its ID as 0x5690 */ #define OV5693_CHIP_ID 0x5690 /* Exposure */ -#define OV5693_EXPOSURE_CTRL_REG OV5693_REG_24BIT(0x3500) +#define OV5693_EXPOSURE_CTRL_REG CCI_REG24(0x3500) #define OV5693_EXPOSURE_CTRL_MASK GENMASK(19, 4) #define OV5693_INTEGRATION_TIME_MARGIN 8 #define OV5693_EXPOSURE_MIN 1 #define OV5693_EXPOSURE_STEP 1 /* Analogue Gain */ -#define OV5693_GAIN_CTRL_REG OV5693_REG_16BIT(0x350a) +#define OV5693_GAIN_CTRL_REG CCI_REG16(0x350a) #define OV5693_GAIN_CTRL_MASK GENMASK(10, 4) #define OV5693_GAIN_MIN 1 #define OV5693_GAIN_MAX 127 @@ -60,9 +55,9 @@ #define OV5693_GAIN_STEP 1 /* Digital Gain */ -#define OV5693_MWB_RED_GAIN_REG OV5693_REG_16BIT(0x3400) -#define OV5693_MWB_GREEN_GAIN_REG OV5693_REG_16BIT(0x3402) -#define OV5693_MWB_BLUE_GAIN_REG OV5693_REG_16BIT(0x3404) +#define OV5693_MWB_RED_GAIN_REG CCI_REG16(0x3400) +#define OV5693_MWB_GREEN_GAIN_REG CCI_REG16(0x3402) +#define OV5693_MWB_BLUE_GAIN_REG CCI_REG16(0x3404) #define OV5693_MWB_GAIN_MASK GENMASK(11, 0) #define OV5693_MWB_GAIN_MAX 0x0fff #define OV5693_DIGITAL_GAIN_MIN 1 @@ -71,36 +66,36 @@ #define OV5693_DIGITAL_GAIN_STEP 1 /* Timing and Format */ -#define OV5693_CROP_START_X_REG OV5693_REG_16BIT(0x3800) -#define OV5693_CROP_START_Y_REG OV5693_REG_16BIT(0x3802) -#define OV5693_CROP_END_X_REG OV5693_REG_16BIT(0x3804) -#define OV5693_CROP_END_Y_REG OV5693_REG_16BIT(0x3806) -#define OV5693_OUTPUT_SIZE_X_REG OV5693_REG_16BIT(0x3808) -#define OV5693_OUTPUT_SIZE_Y_REG OV5693_REG_16BIT(0x380a) - -#define OV5693_TIMING_HTS_REG OV5693_REG_16BIT(0x380c) +#define OV5693_CROP_START_X_REG CCI_REG16(0x3800) +#define OV5693_CROP_START_Y_REG CCI_REG16(0x3802) +#define OV5693_CROP_END_X_REG CCI_REG16(0x3804) +#define OV5693_CROP_END_Y_REG CCI_REG16(0x3806) +#define OV5693_OUTPUT_SIZE_X_REG CCI_REG16(0x3808) +#define OV5693_OUTPUT_SIZE_Y_REG CCI_REG16(0x380a) + +#define OV5693_TIMING_HTS_REG CCI_REG16(0x380c) #define OV5693_FIXED_PPL 2688U -#define OV5693_TIMING_VTS_REG OV5693_REG_16BIT(0x380e) +#define OV5693_TIMING_VTS_REG CCI_REG16(0x380e) #define OV5693_TIMING_MAX_VTS 0xffff #define OV5693_TIMING_MIN_VTS 0x04 -#define OV5693_OFFSET_START_X_REG OV5693_REG_16BIT(0x3810) -#define OV5693_OFFSET_START_Y_REG OV5693_REG_16BIT(0x3812) +#define OV5693_OFFSET_START_X_REG CCI_REG16(0x3810) +#define OV5693_OFFSET_START_Y_REG CCI_REG16(0x3812) -#define OV5693_SUB_INC_X_REG OV5693_REG_8BIT(0x3814) -#define OV5693_SUB_INC_Y_REG OV5693_REG_8BIT(0x3815) +#define OV5693_SUB_INC_X_REG CCI_REG8(0x3814) +#define OV5693_SUB_INC_Y_REG CCI_REG8(0x3815) -#define OV5693_FORMAT1_REG OV5693_REG_8BIT(0x3820) +#define OV5693_FORMAT1_REG CCI_REG8(0x3820) #define OV5693_FORMAT1_FLIP_VERT_ISP_EN BIT(6) #define OV5693_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1) #define OV5693_FORMAT1_VBIN_EN BIT(0) -#define OV5693_FORMAT2_REG OV5693_REG_8BIT(0x3821) +#define OV5693_FORMAT2_REG CCI_REG8(0x3821) #define OV5693_FORMAT2_HDR_EN BIT(7) #define OV5693_FORMAT2_FLIP_HORZ_ISP_EN BIT(2) #define OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1) #define OV5693_FORMAT2_HBIN_EN BIT(0) -#define OV5693_ISP_CTRL2_REG OV5693_REG_8BIT(0x5002) +#define OV5693_ISP_CTRL2_REG CCI_REG8(0x5002) #define OV5693_ISP_SCALE_ENABLE BIT(7) /* Pixel Array */ @@ -116,7 +111,7 @@ #define OV5693_MIN_CROP_HEIGHT 2 /* Test Pattern */ -#define OV5693_TEST_PATTERN_REG OV5693_REG_8BIT(0x5e00) +#define OV5693_TEST_PATTERN_REG CCI_REG8(0x5e00) #define OV5693_TEST_PATTERN_ENABLE BIT(7) #define OV5693_TEST_PATTERN_ROLLING BIT(6) #define OV5693_TEST_PATTERN_RANDOM 0x01 @@ -137,19 +132,9 @@ static const char * const ov5693_supply_names[] = { #define OV5693_NUM_SUPPLIES ARRAY_SIZE(ov5693_supply_names) -struct ov5693_reg { - u32 reg; - u8 val; -}; - -struct ov5693_reg_list { - u32 num_regs; - const struct ov5693_reg *regs; -}; - struct ov5693_device { - struct i2c_client *client; struct device *dev; + struct regmap *regmap; /* Protect against concurrent changes to controls */ struct mutex lock; @@ -189,156 +174,151 @@ struct ov5693_device { } ctrls; }; -static const struct ov5693_reg ov5693_global_regs[] = { - {OV5693_REG_8BIT(0x3016), 0xf0}, - {OV5693_REG_8BIT(0x3017), 0xf0}, - {OV5693_REG_8BIT(0x3018), 0xf0}, - {OV5693_REG_8BIT(0x3022), 0x01}, - {OV5693_REG_8BIT(0x3028), 0x44}, - {OV5693_REG_8BIT(0x3098), 0x02}, - {OV5693_REG_8BIT(0x3099), 0x19}, - {OV5693_REG_8BIT(0x309a), 0x02}, - {OV5693_REG_8BIT(0x309b), 0x01}, - {OV5693_REG_8BIT(0x309c), 0x00}, - {OV5693_REG_8BIT(0x30a0), 0xd2}, - {OV5693_REG_8BIT(0x30a2), 0x01}, - {OV5693_REG_8BIT(0x30b2), 0x00}, - {OV5693_REG_8BIT(0x30b3), 0x83}, - {OV5693_REG_8BIT(0x30b4), 0x03}, - {OV5693_REG_8BIT(0x30b5), 0x04}, - {OV5693_REG_8BIT(0x30b6), 0x01}, - {OV5693_REG_8BIT(0x3080), 0x01}, - {OV5693_REG_8BIT(0x3104), 0x21}, - {OV5693_REG_8BIT(0x3106), 0x00}, - {OV5693_REG_8BIT(0x3406), 0x01}, - {OV5693_REG_8BIT(0x3503), 0x07}, - {OV5693_REG_8BIT(0x350b), 0x40}, - {OV5693_REG_8BIT(0x3601), 0x0a}, - {OV5693_REG_8BIT(0x3602), 0x38}, - {OV5693_REG_8BIT(0x3612), 0x80}, - {OV5693_REG_8BIT(0x3620), 0x54}, - {OV5693_REG_8BIT(0x3621), 0xc7}, - {OV5693_REG_8BIT(0x3622), 0x0f}, - {OV5693_REG_8BIT(0x3625), 0x10}, - {OV5693_REG_8BIT(0x3630), 0x55}, - {OV5693_REG_8BIT(0x3631), 0xf4}, - {OV5693_REG_8BIT(0x3632), 0x00}, - {OV5693_REG_8BIT(0x3633), 0x34}, - {OV5693_REG_8BIT(0x3634), 0x02}, - {OV5693_REG_8BIT(0x364d), 0x0d}, - {OV5693_REG_8BIT(0x364f), 0xdd}, - {OV5693_REG_8BIT(0x3660), 0x04}, - {OV5693_REG_8BIT(0x3662), 0x10}, - {OV5693_REG_8BIT(0x3663), 0xf1}, - {OV5693_REG_8BIT(0x3665), 0x00}, - {OV5693_REG_8BIT(0x3666), 0x20}, - {OV5693_REG_8BIT(0x3667), 0x00}, - {OV5693_REG_8BIT(0x366a), 0x80}, - {OV5693_REG_8BIT(0x3680), 0xe0}, - {OV5693_REG_8BIT(0x3681), 0x00}, - {OV5693_REG_8BIT(0x3700), 0x42}, - {OV5693_REG_8BIT(0x3701), 0x14}, - {OV5693_REG_8BIT(0x3702), 0xa0}, - {OV5693_REG_8BIT(0x3703), 0xd8}, - {OV5693_REG_8BIT(0x3704), 0x78}, - {OV5693_REG_8BIT(0x3705), 0x02}, - {OV5693_REG_8BIT(0x370a), 0x00}, - {OV5693_REG_8BIT(0x370b), 0x20}, - {OV5693_REG_8BIT(0x370c), 0x0c}, - {OV5693_REG_8BIT(0x370d), 0x11}, - {OV5693_REG_8BIT(0x370e), 0x00}, - {OV5693_REG_8BIT(0x370f), 0x40}, - {OV5693_REG_8BIT(0x3710), 0x00}, - {OV5693_REG_8BIT(0x371a), 0x1c}, - {OV5693_REG_8BIT(0x371b), 0x05}, - {OV5693_REG_8BIT(0x371c), 0x01}, - {OV5693_REG_8BIT(0x371e), 0xa1}, - {OV5693_REG_8BIT(0x371f), 0x0c}, - {OV5693_REG_8BIT(0x3721), 0x00}, - {OV5693_REG_8BIT(0x3724), 0x10}, - {OV5693_REG_8BIT(0x3726), 0x00}, - {OV5693_REG_8BIT(0x372a), 0x01}, - {OV5693_REG_8BIT(0x3730), 0x10}, - {OV5693_REG_8BIT(0x3738), 0x22}, - {OV5693_REG_8BIT(0x3739), 0xe5}, - {OV5693_REG_8BIT(0x373a), 0x50}, - {OV5693_REG_8BIT(0x373b), 0x02}, - {OV5693_REG_8BIT(0x373c), 0x41}, - {OV5693_REG_8BIT(0x373f), 0x02}, - {OV5693_REG_8BIT(0x3740), 0x42}, - {OV5693_REG_8BIT(0x3741), 0x02}, - {OV5693_REG_8BIT(0x3742), 0x18}, - {OV5693_REG_8BIT(0x3743), 0x01}, - {OV5693_REG_8BIT(0x3744), 0x02}, - {OV5693_REG_8BIT(0x3747), 0x10}, - {OV5693_REG_8BIT(0x374c), 0x04}, - {OV5693_REG_8BIT(0x3751), 0xf0}, - {OV5693_REG_8BIT(0x3752), 0x00}, - {OV5693_REG_8BIT(0x3753), 0x00}, - {OV5693_REG_8BIT(0x3754), 0xc0}, - {OV5693_REG_8BIT(0x3755), 0x00}, - {OV5693_REG_8BIT(0x3756), 0x1a}, - {OV5693_REG_8BIT(0x3758), 0x00}, - {OV5693_REG_8BIT(0x3759), 0x0f}, - {OV5693_REG_8BIT(0x376b), 0x44}, - {OV5693_REG_8BIT(0x375c), 0x04}, - {OV5693_REG_8BIT(0x3774), 0x10}, - {OV5693_REG_8BIT(0x3776), 0x00}, - {OV5693_REG_8BIT(0x377f), 0x08}, - {OV5693_REG_8BIT(0x3780), 0x22}, - {OV5693_REG_8BIT(0x3781), 0x0c}, - {OV5693_REG_8BIT(0x3784), 0x2c}, - {OV5693_REG_8BIT(0x3785), 0x1e}, - {OV5693_REG_8BIT(0x378f), 0xf5}, - {OV5693_REG_8BIT(0x3791), 0xb0}, - {OV5693_REG_8BIT(0x3795), 0x00}, - {OV5693_REG_8BIT(0x3796), 0x64}, - {OV5693_REG_8BIT(0x3797), 0x11}, - {OV5693_REG_8BIT(0x3798), 0x30}, - {OV5693_REG_8BIT(0x3799), 0x41}, - {OV5693_REG_8BIT(0x379a), 0x07}, - {OV5693_REG_8BIT(0x379b), 0xb0}, - {OV5693_REG_8BIT(0x379c), 0x0c}, - {OV5693_REG_8BIT(0x3a04), 0x06}, - {OV5693_REG_8BIT(0x3a05), 0x14}, - {OV5693_REG_8BIT(0x3e07), 0x20}, - {OV5693_REG_8BIT(0x4000), 0x08}, - {OV5693_REG_8BIT(0x4001), 0x04}, - {OV5693_REG_8BIT(0x4004), 0x08}, - {OV5693_REG_8BIT(0x4006), 0x20}, - {OV5693_REG_8BIT(0x4008), 0x24}, - {OV5693_REG_8BIT(0x4009), 0x10}, - {OV5693_REG_8BIT(0x4058), 0x00}, - {OV5693_REG_8BIT(0x4101), 0xb2}, - {OV5693_REG_8BIT(0x4307), 0x31}, - {OV5693_REG_8BIT(0x4511), 0x05}, - {OV5693_REG_8BIT(0x4512), 0x01}, - {OV5693_REG_8BIT(0x481f), 0x30}, - {OV5693_REG_8BIT(0x4826), 0x2c}, - {OV5693_REG_8BIT(0x4d02), 0xfd}, - {OV5693_REG_8BIT(0x4d03), 0xf5}, - {OV5693_REG_8BIT(0x4d04), 0x0c}, - {OV5693_REG_8BIT(0x4d05), 0xcc}, - {OV5693_REG_8BIT(0x4837), 0x0a}, - {OV5693_REG_8BIT(0x5003), 0x20}, - {OV5693_REG_8BIT(0x5013), 0x00}, - {OV5693_REG_8BIT(0x5842), 0x01}, - {OV5693_REG_8BIT(0x5843), 0x2b}, - {OV5693_REG_8BIT(0x5844), 0x01}, - {OV5693_REG_8BIT(0x5845), 0x92}, - {OV5693_REG_8BIT(0x5846), 0x01}, - {OV5693_REG_8BIT(0x5847), 0x8f}, - {OV5693_REG_8BIT(0x5848), 0x01}, - {OV5693_REG_8BIT(0x5849), 0x0c}, - {OV5693_REG_8BIT(0x5e10), 0x0c}, - {OV5693_REG_8BIT(0x3820), 0x00}, - {OV5693_REG_8BIT(0x3821), 0x1e}, - {OV5693_REG_8BIT(0x5041), 0x14} -}; - -static const struct ov5693_reg_list ov5693_global_setting = { - .num_regs = ARRAY_SIZE(ov5693_global_regs), - .regs = ov5693_global_regs, +static const struct cci_reg_sequence ov5693_global_regs[] = { + {CCI_REG8(0x3016), 0xf0}, + {CCI_REG8(0x3017), 0xf0}, + {CCI_REG8(0x3018), 0xf0}, + {CCI_REG8(0x3022), 0x01}, + {CCI_REG8(0x3028), 0x44}, + {CCI_REG8(0x3098), 0x02}, + {CCI_REG8(0x3099), 0x19}, + {CCI_REG8(0x309a), 0x02}, + {CCI_REG8(0x309b), 0x01}, + {CCI_REG8(0x309c), 0x00}, + {CCI_REG8(0x30a0), 0xd2}, + {CCI_REG8(0x30a2), 0x01}, + {CCI_REG8(0x30b2), 0x00}, + {CCI_REG8(0x30b3), 0x83}, + {CCI_REG8(0x30b4), 0x03}, + {CCI_REG8(0x30b5), 0x04}, + {CCI_REG8(0x30b6), 0x01}, + {CCI_REG8(0x3080), 0x01}, + {CCI_REG8(0x3104), 0x21}, + {CCI_REG8(0x3106), 0x00}, + {CCI_REG8(0x3406), 0x01}, + {CCI_REG8(0x3503), 0x07}, + {CCI_REG8(0x350b), 0x40}, + {CCI_REG8(0x3601), 0x0a}, + {CCI_REG8(0x3602), 0x38}, + {CCI_REG8(0x3612), 0x80}, + {CCI_REG8(0x3620), 0x54}, + {CCI_REG8(0x3621), 0xc7}, + {CCI_REG8(0x3622), 0x0f}, + {CCI_REG8(0x3625), 0x10}, + {CCI_REG8(0x3630), 0x55}, + {CCI_REG8(0x3631), 0xf4}, + {CCI_REG8(0x3632), 0x00}, + {CCI_REG8(0x3633), 0x34}, + {CCI_REG8(0x3634), 0x02}, + {CCI_REG8(0x364d), 0x0d}, + {CCI_REG8(0x364f), 0xdd}, + {CCI_REG8(0x3660), 0x04}, + {CCI_REG8(0x3662), 0x10}, + {CCI_REG8(0x3663), 0xf1}, + {CCI_REG8(0x3665), 0x00}, + {CCI_REG8(0x3666), 0x20}, + {CCI_REG8(0x3667), 0x00}, + {CCI_REG8(0x366a), 0x80}, + {CCI_REG8(0x3680), 0xe0}, + {CCI_REG8(0x3681), 0x00}, + {CCI_REG8(0x3700), 0x42}, + {CCI_REG8(0x3701), 0x14}, + {CCI_REG8(0x3702), 0xa0}, + {CCI_REG8(0x3703), 0xd8}, + {CCI_REG8(0x3704), 0x78}, + {CCI_REG8(0x3705), 0x02}, + {CCI_REG8(0x370a), 0x00}, + {CCI_REG8(0x370b), 0x20}, + {CCI_REG8(0x370c), 0x0c}, + {CCI_REG8(0x370d), 0x11}, + {CCI_REG8(0x370e), 0x00}, + {CCI_REG8(0x370f), 0x40}, + {CCI_REG8(0x3710), 0x00}, + {CCI_REG8(0x371a), 0x1c}, + {CCI_REG8(0x371b), 0x05}, + {CCI_REG8(0x371c), 0x01}, + {CCI_REG8(0x371e), 0xa1}, + {CCI_REG8(0x371f), 0x0c}, + {CCI_REG8(0x3721), 0x00}, + {CCI_REG8(0x3724), 0x10}, + {CCI_REG8(0x3726), 0x00}, + {CCI_REG8(0x372a), 0x01}, + {CCI_REG8(0x3730), 0x10}, + {CCI_REG8(0x3738), 0x22}, + {CCI_REG8(0x3739), 0xe5}, + {CCI_REG8(0x373a), 0x50}, + {CCI_REG8(0x373b), 0x02}, + {CCI_REG8(0x373c), 0x41}, + {CCI_REG8(0x373f), 0x02}, + {CCI_REG8(0x3740), 0x42}, + {CCI_REG8(0x3741), 0x02}, + {CCI_REG8(0x3742), 0x18}, + {CCI_REG8(0x3743), 0x01}, + {CCI_REG8(0x3744), 0x02}, + {CCI_REG8(0x3747), 0x10}, + {CCI_REG8(0x374c), 0x04}, + {CCI_REG8(0x3751), 0xf0}, + {CCI_REG8(0x3752), 0x00}, + {CCI_REG8(0x3753), 0x00}, + {CCI_REG8(0x3754), 0xc0}, + {CCI_REG8(0x3755), 0x00}, + {CCI_REG8(0x3756), 0x1a}, + {CCI_REG8(0x3758), 0x00}, + {CCI_REG8(0x3759), 0x0f}, + {CCI_REG8(0x376b), 0x44}, + {CCI_REG8(0x375c), 0x04}, + {CCI_REG8(0x3774), 0x10}, + {CCI_REG8(0x3776), 0x00}, + {CCI_REG8(0x377f), 0x08}, + {CCI_REG8(0x3780), 0x22}, + {CCI_REG8(0x3781), 0x0c}, + {CCI_REG8(0x3784), 0x2c}, + {CCI_REG8(0x3785), 0x1e}, + {CCI_REG8(0x378f), 0xf5}, + {CCI_REG8(0x3791), 0xb0}, + {CCI_REG8(0x3795), 0x00}, + {CCI_REG8(0x3796), 0x64}, + {CCI_REG8(0x3797), 0x11}, + {CCI_REG8(0x3798), 0x30}, + {CCI_REG8(0x3799), 0x41}, + {CCI_REG8(0x379a), 0x07}, + {CCI_REG8(0x379b), 0xb0}, + {CCI_REG8(0x379c), 0x0c}, + {CCI_REG8(0x3a04), 0x06}, + {CCI_REG8(0x3a05), 0x14}, + {CCI_REG8(0x3e07), 0x20}, + {CCI_REG8(0x4000), 0x08}, + {CCI_REG8(0x4001), 0x04}, + {CCI_REG8(0x4004), 0x08}, + {CCI_REG8(0x4006), 0x20}, + {CCI_REG8(0x4008), 0x24}, + {CCI_REG8(0x4009), 0x10}, + {CCI_REG8(0x4058), 0x00}, + {CCI_REG8(0x4101), 0xb2}, + {CCI_REG8(0x4307), 0x31}, + {CCI_REG8(0x4511), 0x05}, + {CCI_REG8(0x4512), 0x01}, + {CCI_REG8(0x481f), 0x30}, + {CCI_REG8(0x4826), 0x2c}, + {CCI_REG8(0x4d02), 0xfd}, + {CCI_REG8(0x4d03), 0xf5}, + {CCI_REG8(0x4d04), 0x0c}, + {CCI_REG8(0x4d05), 0xcc}, + {CCI_REG8(0x4837), 0x0a}, + {CCI_REG8(0x5003), 0x20}, + {CCI_REG8(0x5013), 0x00}, + {CCI_REG8(0x5842), 0x01}, + {CCI_REG8(0x5843), 0x2b}, + {CCI_REG8(0x5844), 0x01}, + {CCI_REG8(0x5845), 0x92}, + {CCI_REG8(0x5846), 0x01}, + {CCI_REG8(0x5847), 0x8f}, + {CCI_REG8(0x5848), 0x01}, + {CCI_REG8(0x5849), 0x0c}, + {CCI_REG8(0x5e10), 0x0c}, + {CCI_REG8(0x3820), 0x00}, + {CCI_REG8(0x3821), 0x1e}, + {CCI_REG8(0x5041), 0x14} }; static const struct v4l2_rect ov5693_default_crop = { @@ -373,115 +353,6 @@ static const u8 ov5693_test_pattern_bits[] = { OV5693_TEST_PATTERN_ROLLING, }; -/* I2C I/O Operations */ - -static int ov5693_read_reg(struct ov5693_device *ov5693, u32 addr, u32 *value) -{ - struct i2c_client *client = ov5693->client; - __be16 reg; - u8 val[4]; - struct i2c_msg msg[] = { - { - .addr = client->addr, - .flags = 0, - .len = 2, - .buf = (u8 *)®, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .buf = (u8 *)&val, - }, - }; - unsigned int len = ((addr >> OV5693_REG_SIZE_SHIFT) & 3); - unsigned int i; - int ret; - - reg = cpu_to_be16(addr & OV5693_REG_ADDR_MASK); - - msg[1].len = len; - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret < 0) - return dev_err_probe(&client->dev, ret, - "Failed to read register 0x%04x\n", - addr & OV5693_REG_ADDR_MASK); - - *value = 0; - for (i = 0; i < len; ++i) { - *value <<= 8; - *value |= val[i]; - } - - return 0; -} - -static void ov5693_write_reg(struct ov5693_device *ov5693, u32 addr, u32 value, - int *error) -{ - struct i2c_client *client = ov5693->client; - struct { - __be16 reg; - u8 val[4]; - } __packed buf; - struct i2c_msg msg = { - .addr = client->addr, - .buf = (u8 *)&buf, - }; - unsigned int len = ((addr >> OV5693_REG_SIZE_SHIFT) & 3); - unsigned int i; - int ret; - - if (*error < 0) - return; - - buf.reg = cpu_to_be16(addr & OV5693_REG_ADDR_MASK); - for (i = 0; i < len; ++i) { - buf.val[len - i - 1] = value & 0xff; - value >>= 8; - } - - msg.len = len + 2; - - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret < 0) { - dev_err(&client->dev, "Failed to write register 0x%04x: %d\n", - addr & OV5693_REG_ADDR_MASK, ret); - *error = ret; - } -} - -static int ov5693_write_reg_array(struct ov5693_device *ov5693, - const struct ov5693_reg_list *reglist) -{ - unsigned int i; - int ret = 0; - - for (i = 0; i < reglist->num_regs; i++) - ov5693_write_reg(ov5693, reglist->regs[i].reg, - reglist->regs[i].val, &ret); - - return ret; -} - -static int ov5693_update_bits(struct ov5693_device *ov5693, u32 address, - u32 mask, u32 bits) -{ - u32 value = 0; - int ret; - - ret = ov5693_read_reg(ov5693, address, &value); - if (ret) - return ret; - - value &= ~mask; - value |= bits; - - ov5693_write_reg(ov5693, address, value, &ret); - - return ret; -} - /* V4L2 Controls Functions */ static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, @@ -491,8 +362,8 @@ static int ov5693_flip_vert_configure(struct ov5693_device *ov5693, OV5693_FORMAT1_FLIP_VERT_SENSOR_EN; int ret; - ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, bits, - enable ? bits : 0); + ret = cci_update_bits(ov5693->regmap, OV5693_FORMAT1_REG, bits, + enable ? bits : 0, NULL); if (ret) return ret; @@ -506,8 +377,8 @@ static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, OV5693_FORMAT2_FLIP_HORZ_SENSOR_EN; int ret; - ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, bits, - enable ? bits : 0); + ret = cci_update_bits(ov5693->regmap, OV5693_FORMAT2_REG, bits, + enable ? bits : 0, NULL); if (ret) return ret; @@ -516,10 +387,11 @@ static int ov5693_flip_horz_configure(struct ov5693_device *ov5693, static int ov5693_get_exposure(struct ov5693_device *ov5693, s32 *value) { - u32 exposure; + u64 exposure; int ret; - ret = ov5693_read_reg(ov5693, OV5693_EXPOSURE_CTRL_REG, &exposure); + ret = cci_read(ov5693->regmap, OV5693_EXPOSURE_CTRL_REG, &exposure, + NULL); if (ret) return ret; @@ -536,17 +408,17 @@ static int ov5693_exposure_configure(struct ov5693_device *ov5693, exposure = (exposure << 4) & OV5693_EXPOSURE_CTRL_MASK; - ov5693_write_reg(ov5693, OV5693_EXPOSURE_CTRL_REG, exposure, &ret); + cci_write(ov5693->regmap, OV5693_EXPOSURE_CTRL_REG, exposure, &ret); return ret; } static int ov5693_get_gain(struct ov5693_device *ov5693, u32 *gain) { - u32 value; + u64 value; int ret; - ret = ov5693_read_reg(ov5693, OV5693_GAIN_CTRL_REG, &value); + ret = cci_read(ov5693->regmap, OV5693_GAIN_CTRL_REG, &value, NULL); if (ret) return ret; @@ -563,9 +435,9 @@ static int ov5693_digital_gain_configure(struct ov5693_device *ov5693, gain &= OV5693_MWB_GAIN_MASK; - ov5693_write_reg(ov5693, OV5693_MWB_RED_GAIN_REG, gain, &ret); - ov5693_write_reg(ov5693, OV5693_MWB_GREEN_GAIN_REG, gain, &ret); - ov5693_write_reg(ov5693, OV5693_MWB_BLUE_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_RED_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_GREEN_GAIN_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_MWB_BLUE_GAIN_REG, gain, &ret); return ret; } @@ -576,7 +448,7 @@ static int ov5693_analog_gain_configure(struct ov5693_device *ov5693, u32 gain) gain = (gain << 4) & OV5693_GAIN_CTRL_MASK; - ov5693_write_reg(ov5693, OV5693_GAIN_CTRL_REG, gain, &ret); + cci_write(ov5693->regmap, OV5693_GAIN_CTRL_REG, gain, &ret); return ret; } @@ -586,7 +458,7 @@ static int ov5693_vts_configure(struct ov5693_device *ov5693, u32 vblank) u16 vts = ov5693->mode.format.height + vblank; int ret = 0; - ov5693_write_reg(ov5693, OV5693_TIMING_VTS_REG, vts, &ret); + cci_write(ov5693->regmap, OV5693_TIMING_VTS_REG, vts, &ret); return ret; } @@ -595,8 +467,8 @@ static int ov5693_test_pattern_configure(struct ov5693_device *ov5693, u32 idx) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_TEST_PATTERN_REG, - ov5693_test_pattern_bits[idx], &ret); + cci_write(ov5693->regmap, OV5693_TEST_PATTERN_REG, + ov5693_test_pattern_bits[idx], &ret); return ret; } @@ -685,59 +557,54 @@ static int ov5693_mode_configure(struct ov5693_device *ov5693) int ret = 0; /* Crop Start X */ - ov5693_write_reg(ov5693, OV5693_CROP_START_X_REG, mode->crop.left, - &ret); + cci_write(ov5693->regmap, OV5693_CROP_START_X_REG, mode->crop.left, + &ret); /* Offset X */ - ov5693_write_reg(ov5693, OV5693_OFFSET_START_X_REG, 0, &ret); + cci_write(ov5693->regmap, OV5693_OFFSET_START_X_REG, 0, &ret); /* Output Size X */ - ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_X_REG, mode->format.width, - &ret); + cci_write(ov5693->regmap, OV5693_OUTPUT_SIZE_X_REG, mode->format.width, + &ret); /* Crop End X */ - ov5693_write_reg(ov5693, OV5693_CROP_END_X_REG, - mode->crop.left + mode->crop.width, &ret); + cci_write(ov5693->regmap, OV5693_CROP_END_X_REG, + mode->crop.left + mode->crop.width, &ret); /* Horizontal Total Size */ - ov5693_write_reg(ov5693, OV5693_TIMING_HTS_REG, OV5693_FIXED_PPL, - &ret); + cci_write(ov5693->regmap, OV5693_TIMING_HTS_REG, OV5693_FIXED_PPL, + &ret); /* Crop Start Y */ - ov5693_write_reg(ov5693, OV5693_CROP_START_Y_REG, mode->crop.top, - &ret); + cci_write(ov5693->regmap, OV5693_CROP_START_Y_REG, mode->crop.top, + &ret); /* Offset Y */ - ov5693_write_reg(ov5693, OV5693_OFFSET_START_Y_REG, 0, &ret); + cci_write(ov5693->regmap, OV5693_OFFSET_START_Y_REG, 0, &ret); /* Output Size Y */ - ov5693_write_reg(ov5693, OV5693_OUTPUT_SIZE_Y_REG, mode->format.height, - &ret); + cci_write(ov5693->regmap, OV5693_OUTPUT_SIZE_Y_REG, mode->format.height, + &ret); /* Crop End Y */ - ov5693_write_reg(ov5693, OV5693_CROP_END_Y_REG, - mode->crop.top + mode->crop.height, &ret); + cci_write(ov5693->regmap, OV5693_CROP_END_Y_REG, + mode->crop.top + mode->crop.height, &ret); /* Subsample X increase */ - ov5693_write_reg(ov5693, OV5693_SUB_INC_X_REG, - ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret); + cci_write(ov5693->regmap, OV5693_SUB_INC_X_REG, + ((mode->inc_x_odd << 4) & 0xf0) | 0x01, &ret); /* Subsample Y increase */ - ov5693_write_reg(ov5693, OV5693_SUB_INC_Y_REG, - ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret); - - if (ret) - return ret; + cci_write(ov5693->regmap, OV5693_SUB_INC_Y_REG, + ((mode->inc_y_odd << 4) & 0xf0) | 0x01, &ret); /* Binning */ - ret = ov5693_update_bits(ov5693, OV5693_FORMAT1_REG, - OV5693_FORMAT1_VBIN_EN, - mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0); - if (ret) - return ret; + cci_update_bits(ov5693->regmap, OV5693_FORMAT1_REG, + OV5693_FORMAT1_VBIN_EN, + mode->binning_y ? OV5693_FORMAT1_VBIN_EN : 0, &ret); - ret = ov5693_update_bits(ov5693, OV5693_FORMAT2_REG, - OV5693_FORMAT2_HBIN_EN, - mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0); + cci_update_bits(ov5693->regmap, OV5693_FORMAT2_REG, + OV5693_FORMAT2_HBIN_EN, + mode->binning_x ? OV5693_FORMAT2_HBIN_EN : 0, &ret); return ret; } @@ -746,9 +613,9 @@ static int ov5693_enable_streaming(struct ov5693_device *ov5693, bool enable) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_SW_STREAM_REG, - enable ? OV5693_START_STREAMING : - OV5693_STOP_STREAMING, &ret); + cci_write(ov5693->regmap, OV5693_SW_STREAM_REG, + enable ? OV5693_START_STREAMING : OV5693_STOP_STREAMING, + &ret); return ret; } @@ -757,7 +624,7 @@ static int ov5693_sw_reset(struct ov5693_device *ov5693) { int ret = 0; - ov5693_write_reg(ov5693, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret); + cci_write(ov5693->regmap, OV5693_SW_RESET_REG, OV5693_SW_RESET, &ret); return ret; } @@ -771,7 +638,8 @@ static int ov5693_sensor_init(struct ov5693_device *ov5693) return dev_err_probe(ov5693->dev, ret, "software reset error\n"); - ret = ov5693_write_reg_array(ov5693, &ov5693_global_setting); + ret = cci_multi_reg_write(ov5693->regmap, ov5693_global_regs, + ARRAY_SIZE(ov5693_global_regs), NULL); if (ret) return dev_err_probe(ov5693->dev, ret, "global settings error\n"); @@ -871,15 +739,15 @@ out_unlock: static int ov5693_detect(struct ov5693_device *ov5693) { int ret; - u32 id; + u64 id; - ret = ov5693_read_reg(ov5693, OV5693_REG_CHIP_ID, &id); + ret = cci_read(ov5693->regmap, OV5693_REG_CHIP_ID, &id, NULL); if (ret) return ret; if (id != OV5693_CHIP_ID) return dev_err_probe(ov5693->dev, -ENODEV, - "sensor ID mismatch. Found 0x%04x\n", id); + "sensor ID mismatch. Got 0x%04llx\n", id); return 0; } @@ -1407,9 +1275,12 @@ static int ov5693_probe(struct i2c_client *client) if (!ov5693) return -ENOMEM; - ov5693->client = client; ov5693->dev = &client->dev; + ov5693->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov5693->regmap)) + return PTR_ERR(ov5693->regmap); + ret = ov5693_check_hwcfg(ov5693); if (ret) return ret; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 10e47c7d4e0c..dffdb475e433 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1210,7 +1210,7 @@ static struct i2c_driver ov7740_i2c_driver = { .driver = { .name = "ov7740", .pm = &ov7740_pm_ops, - .of_match_table = of_match_ptr(ov7740_of_match), + .of_match_table = ov7740_of_match, }, .probe = ov7740_probe, .remove = ov7740_remove, diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index 01a2596282f0..f4e2e2f3972a 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -567,7 +567,6 @@ again: static int rdacm20_probe(struct i2c_client *client) { struct rdacm20_device *dev; - struct fwnode_handle *ep; int ret; dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); @@ -616,24 +615,12 @@ static int rdacm20_probe(struct i2c_client *client) if (ret < 0) goto error_free_ctrls; - ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); - if (!ep) { - dev_err(&client->dev, - "Unable to get endpoint in node %pOF\n", - client->dev.of_node); - ret = -ENOENT; - goto error_free_ctrls; - } - dev->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&dev->sd); if (ret) - goto error_put_node; + goto error_free_ctrls; return 0; -error_put_node: - fwnode_handle_put(ep); error_free_ctrls: v4l2_ctrl_handler_free(&dev->ctrls); error: @@ -650,7 +637,6 @@ static void rdacm20_remove(struct i2c_client *client) { struct rdacm20_device *dev = i2c_to_rdacm20(client); - fwnode_handle_put(dev->sd.fwnode); v4l2_async_unregister_subdev(&dev->sd); v4l2_ctrl_handler_free(&dev->ctrls); media_entity_cleanup(&dev->sd.entity); diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index 043fec778a5e..a36a709243fd 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -351,7 +351,7 @@ static void ov10640_power_up(struct rdacm21_device *dev) static int ov10640_check_id(struct rdacm21_device *dev) { unsigned int i; - u8 val; + u8 val = 0; /* Read OV10640 ID to test communications. */ for (i = 0; i < OV10640_PID_TIMEOUT; ++i) { @@ -543,7 +543,6 @@ static int rdacm21_initialize(struct rdacm21_device *dev) static int rdacm21_probe(struct i2c_client *client) { struct rdacm21_device *dev; - struct fwnode_handle *ep; int ret; dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); @@ -588,24 +587,12 @@ static int rdacm21_probe(struct i2c_client *client) if (ret < 0) goto error_free_ctrls; - ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); - if (!ep) { - dev_err(&client->dev, - "Unable to get endpoint in node %pOF\n", - client->dev.of_node); - ret = -ENOENT; - goto error_free_ctrls; - } - dev->sd.fwnode = ep; - ret = v4l2_async_register_subdev(&dev->sd); if (ret) - goto error_put_node; + goto error_free_ctrls; return 0; -error_put_node: - fwnode_handle_put(dev->sd.fwnode); error_free_ctrls: v4l2_ctrl_handler_free(&dev->ctrls); error: diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c index 906553a28676..fa27638edc07 100644 --- a/drivers/media/i2c/st-mipid02.c +++ b/drivers/media/i2c/st-mipid02.c @@ -545,7 +545,14 @@ static int mipid02_configure_from_code(struct mipid02_dev *bridge) static int mipid02_stream_disable(struct mipid02_dev *bridge) { struct i2c_client *client = bridge->i2c_client; - int ret; + int ret = -EINVAL; + + if (!bridge->s_subdev) + goto error; + + ret = v4l2_subdev_call(bridge->s_subdev, video, s_stream, 0); + if (ret) + goto error; /* Disable all lanes */ ret = mipid02_write_reg(bridge, MIPID02_CLK_LANE_REG1, 0); @@ -633,6 +640,10 @@ static int mipid02_stream_enable(struct mipid02_dev *bridge) if (ret) goto error; + ret = v4l2_subdev_call(bridge->s_subdev, video, s_stream, 1); + if (ret) + goto error; + return 0; error: @@ -829,7 +840,7 @@ static const struct media_entity_operations mipid02_subdev_entity_ops = { static int mipid02_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); struct i2c_client *client = bridge->i2c_client; @@ -863,7 +874,7 @@ static int mipid02_async_bound(struct v4l2_async_notifier *notifier, static void mipid02_async_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *s_subdev, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct mipid02_dev *bridge = to_mipid02_dev(notifier->sd); @@ -879,7 +890,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) { struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct i2c_client *client = bridge->i2c_client; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct device_node *ep_node; int ret; @@ -911,10 +922,10 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) bridge->rx = ep; /* register async notifier so we get noticed when sensor is connected */ - v4l2_async_nf_init(&bridge->notifier); + v4l2_async_subdev_nf_init(&bridge->notifier, &bridge->sd); asd = v4l2_async_nf_add_fwnode_remote(&bridge->notifier, of_fwnode_handle(ep_node), - struct v4l2_async_subdev); + struct v4l2_async_connection); of_node_put(ep_node); if (IS_ERR(asd)) { @@ -924,7 +935,7 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge) } bridge->notifier.ops = &mipid02_notifier_ops; - ret = v4l2_async_subdev_nf_register(&bridge->sd, &bridge->notifier); + ret = v4l2_async_nf_register(&bridge->notifier); if (ret) v4l2_async_nf_cleanup(&bridge->notifier); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 15f8163be9bf..2785935da497 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -133,8 +133,8 @@ static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (err != ARRAY_SIZE(msgs)) { - v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", - __func__, reg, client->addr); + v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed: %d\n", + __func__, reg, client->addr, err); } } @@ -165,8 +165,8 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) err = i2c_transfer(client->adapter, &msg, 1); if (err != 1) { - v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", - __func__, reg, client->addr); + v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed: %d\n", + __func__, reg, client->addr, err); return; } diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 3f7e147ef594..566f5eaddd57 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -1426,7 +1426,7 @@ static int tc358746_init_controls(struct tc358746 *tc358746) static int tc358746_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) + struct v4l2_async_connection *asd) { struct tc358746 *tc358746 = container_of(notifier, struct tc358746, notifier); @@ -1445,7 +1445,7 @@ static int tc358746_async_register(struct tc358746 *tc358746) struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL, }; - struct v4l2_async_subdev *asd; + struct v4l2_async_connection *asd; struct fwnode_handle *ep; int err; @@ -1460,9 +1460,9 @@ static int tc358746_async_register(struct tc358746 *tc358746) return err; } - v4l2_async_nf_init(&tc358746->notifier); + v4l2_async_subdev_nf_init(&tc358746->notifier, &tc358746->sd); asd = v4l2_async_nf_add_fwnode_remote(&tc358746->notifier, ep, - struct v4l2_async_subdev); + struct v4l2_async_connection); fwnode_handle_put(ep); if (IS_ERR(asd)) { @@ -1472,13 +1472,10 @@ static int tc358746_async_register(struct tc358746 *tc358746) tc358746->notifier.ops = &tc358746_notify_ops; - err = v4l2_async_subdev_nf_register(&tc358746->sd, &tc358746->notifier); + err = v4l2_async_nf_register(&tc358746->notifier); if (err) goto err_cleanup; - tc358746->sd.fwnode = fwnode_graph_get_endpoint_by_id( - dev_fwnode(tc358746->sd.dev), TC358746_SOURCE, 0, 0); - err = v4l2_async_register_subdev(&tc358746->sd); if (err) goto err_unregister; @@ -1486,7 +1483,6 @@ static int tc358746_async_register(struct tc358746 *tc358746) return 0; err_unregister: - fwnode_handle_put(tc358746->sd.fwnode); v4l2_async_nf_unregister(&tc358746->notifier); err_cleanup: v4l2_async_nf_cleanup(&tc358746->notifier); @@ -1605,7 +1601,6 @@ static void tc358746_remove(struct i2c_client *client) v4l2_fwnode_endpoint_free(&tc358746->csi_vep); v4l2_async_nf_unregister(&tc358746->notifier); v4l2_async_nf_cleanup(&tc358746->notifier); - fwnode_handle_put(sd->fwnode); v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index c7fb35ee3f9d..e543b3f7a4d8 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2068,6 +2068,10 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) tvpc->ent.name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", v4l2c->name, v4l2c->label ? v4l2c->label : ""); + if (!tvpc->ent.name) { + ret = -ENOMEM; + goto err_free; + } } ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 6f98abc7ccc1..537ebd9fa8d7 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -16,9 +16,9 @@ #include <linux/kthread.h> #include <linux/i2c.h> #include <linux/list.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/nvmem-provider.h> #include <linux/regmap.h> |