mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
media: i2c: sensor drivers synchronize with kernel 4.4
kernel 4.4 commit ends f3e1785b1893c4a7bdf543ae048b92f9696daf53 Change-Id: I666049f58cb9939e76cbe889d3f85be012ea0fc1 Signed-off-by: Zefa Chen <zefa.chen@rock-chips.com>
This commit is contained in:
@@ -596,6 +596,17 @@ config VIDEO_APTINA_PLL
|
|||||||
config VIDEO_SMIAPP_PLL
|
config VIDEO_SMIAPP_PLL
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config VIDEO_IMX219
|
||||||
|
tristate "Sony IMX219 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the Sony
|
||||||
|
IMX219 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called imx219.
|
||||||
|
|
||||||
config VIDEO_IMX258
|
config VIDEO_IMX258
|
||||||
tristate "Sony IMX258 sensor support"
|
tristate "Sony IMX258 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
@@ -607,6 +618,14 @@ config VIDEO_IMX258
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called imx258.
|
module will be called imx258.
|
||||||
|
|
||||||
|
config VIDEO_IMX258_EEPROM
|
||||||
|
tristate "Sony imx258 sensor otp from eeprom support"
|
||||||
|
depends on VIDEO_V4L2 && I2C
|
||||||
|
depends on VIDEO_IMX258
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
This driver supports IMX258 OTP load from eeprom.
|
||||||
|
|
||||||
config VIDEO_IMX274
|
config VIDEO_IMX274
|
||||||
tristate "Sony IMX274 sensor support"
|
tristate "Sony IMX274 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
@@ -616,6 +635,39 @@ config VIDEO_IMX274
|
|||||||
This is a V4L2 sensor driver for the Sony IMX274
|
This is a V4L2 sensor driver for the Sony IMX274
|
||||||
CMOS image sensor.
|
CMOS image sensor.
|
||||||
|
|
||||||
|
config VIDEO_IMX317
|
||||||
|
tristate "Sony IMX317 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the Sony
|
||||||
|
IMX317 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called imx317.
|
||||||
|
|
||||||
|
config VIDEO_IMX323
|
||||||
|
tristate "Sony IMX323 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the Sony
|
||||||
|
IMX323 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called imx323.
|
||||||
|
|
||||||
|
config VIDEO_IMX327
|
||||||
|
tristate "Sony IMX327 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the Sony
|
||||||
|
IMX327 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called imx327.
|
||||||
|
|
||||||
config VIDEO_OV2640
|
config VIDEO_OV2640
|
||||||
tristate "OmniVision OV2640 sensor support"
|
tristate "OmniVision OV2640 sensor support"
|
||||||
depends on VIDEO_V4L2 && I2C
|
depends on VIDEO_V4L2 && I2C
|
||||||
@@ -663,6 +715,42 @@ config VIDEO_OV2685
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ov2685.
|
module will be called ov2685.
|
||||||
|
|
||||||
|
config VIDEO_OV2718
|
||||||
|
tristate "OmniVision OV2718 sensor support"
|
||||||
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV2718 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov2718.
|
||||||
|
|
||||||
|
config VIDEO_OV2735
|
||||||
|
tristate "OmniVision OV2735 sensor support"
|
||||||
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV2735 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov2735.
|
||||||
|
|
||||||
|
config VIDEO_OV4689
|
||||||
|
tristate "OmniVision OV4689 sensor support"
|
||||||
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV4689 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov4689.
|
||||||
|
|
||||||
config VIDEO_OV5640
|
config VIDEO_OV5640
|
||||||
tristate "OmniVision OV5640 sensor support"
|
tristate "OmniVision OV5640 sensor support"
|
||||||
depends on OF
|
depends on OF
|
||||||
@@ -698,24 +786,24 @@ config VIDEO_OV5647
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ov5647.
|
module will be called ov5647.
|
||||||
|
|
||||||
config VIDEO_OV6650
|
config VIDEO_OV5648
|
||||||
tristate "OmniVision OV6650 sensor support"
|
tristate "OmniVision OV5648 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
depends on MEDIA_CAMERA_SUPPORT
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
---help---
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
This is a Video4Linux2 sensor driver for the OmniVision
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
OV6650 camera.
|
OV5648 camera.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ov6650.
|
module will be called ov5648.
|
||||||
|
|
||||||
config VIDEO_OV5670
|
config VIDEO_OV5670
|
||||||
tristate "OmniVision OV5670 sensor support"
|
tristate "OmniVision OV5670 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
depends on MEDIA_CAMERA_SUPPORT
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
depends on MEDIA_CONTROLLER
|
|
||||||
select V4L2_FWNODE
|
select V4L2_FWNODE
|
||||||
---help---
|
help
|
||||||
This is a Video4Linux2 sensor driver for the OmniVision
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
OV5670 camera.
|
OV5670 camera.
|
||||||
|
|
||||||
@@ -724,15 +812,27 @@ config VIDEO_OV5670
|
|||||||
|
|
||||||
config VIDEO_OV5695
|
config VIDEO_OV5695
|
||||||
tristate "OmniVision OV5695 sensor support"
|
tristate "OmniVision OV5695 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2
|
depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
|
||||||
depends on MEDIA_CAMERA_SUPPORT
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
---help---
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
This is a Video4Linux2 sensor driver for the OmniVision
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
OV5695 camera.
|
OV5695 camera.
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ov5695.
|
module will be called ov5695.
|
||||||
|
|
||||||
|
config VIDEO_OV6650
|
||||||
|
tristate "OmniVision OV6650 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV6650 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov6650.
|
||||||
|
|
||||||
config VIDEO_OV7251
|
config VIDEO_OV7251
|
||||||
tristate "OmniVision OV7251 sensor support"
|
tristate "OmniVision OV7251 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
@@ -785,12 +885,62 @@ config VIDEO_OV7740
|
|||||||
This is a Video4Linux2 sensor driver for the OmniVision
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
OV7740 VGA camera sensor.
|
OV7740 VGA camera sensor.
|
||||||
|
|
||||||
config VIDEO_OV9650
|
config VIDEO_OV7750
|
||||||
tristate "OmniVision OV9650/OV9652 sensor support"
|
tristate "OmniVision OV7750 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
---help---
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
This is a V4L2 sensor driver for the Omnivision
|
depends on MEDIA_CONTROLLER
|
||||||
OV9650 and OV9652 camera sensors.
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV7750 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov7750.
|
||||||
|
|
||||||
|
config VIDEO_OV8858
|
||||||
|
tristate "OmniVision OV8858 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV8858 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov8858.
|
||||||
|
|
||||||
|
config VIDEO_OV9281
|
||||||
|
tristate "OmniVision OV9281 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV9281 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov9281.
|
||||||
|
|
||||||
|
config VIDEO_OV9650
|
||||||
|
tristate "OmniVision OV9650 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV9650 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov9650.
|
||||||
|
|
||||||
|
config VIDEO_OV9750
|
||||||
|
tristate "OmniVision OV9750 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
help
|
||||||
|
This is a Video4Linux2 sensor driver for the OmniVision
|
||||||
|
OV9750 camera.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ov9750.
|
||||||
|
|
||||||
config VIDEO_OV13850
|
config VIDEO_OV13850
|
||||||
tristate "OmniVision OV13850 sensor support"
|
tristate "OmniVision OV13850 sensor support"
|
||||||
@@ -895,6 +1045,17 @@ config VIDEO_MT9V111
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called mt9v111.
|
module will be called mt9v111.
|
||||||
|
|
||||||
|
config VIDEO_AR0230
|
||||||
|
tristate "Aptina AR0230 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the Aptina AR0230 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called ar0230.
|
||||||
|
|
||||||
config VIDEO_SR030PC30
|
config VIDEO_SR030PC30
|
||||||
tristate "Siliconfile SR030PC30 sensor support"
|
tristate "Siliconfile SR030PC30 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2
|
depends on I2C && VIDEO_V4L2
|
||||||
@@ -976,6 +1137,39 @@ config VIDEO_GC0312
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called gc0312.
|
module will be called gc0312.
|
||||||
|
|
||||||
|
config VIDEO_GC0329
|
||||||
|
tristate "GalaxyCore GC0329 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC0329 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc0329.
|
||||||
|
|
||||||
|
config VIDEO_GC0403
|
||||||
|
tristate "GalaxyCore GC0403 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC0403 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc0403.
|
||||||
|
|
||||||
|
config VIDEO_GC2035
|
||||||
|
tristate "GalaxyCore GC2035 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC2035 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc2035.
|
||||||
|
|
||||||
config VIDEO_GC2145
|
config VIDEO_GC2145
|
||||||
tristate "GalaxyCore GC2145 sensor support"
|
tristate "GalaxyCore GC2145 sensor support"
|
||||||
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
@@ -987,6 +1181,116 @@ config VIDEO_GC2145
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called gc2145.
|
module will be called gc2145.
|
||||||
|
|
||||||
|
config VIDEO_GC2155
|
||||||
|
tristate "GalaxyCore GC2155 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC2155 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc2155.
|
||||||
|
|
||||||
|
config VIDEO_GC2355
|
||||||
|
tristate "GalaxyCore GC2355 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC2355 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc2355.
|
||||||
|
|
||||||
|
config VIDEO_GC2385
|
||||||
|
tristate "GalaxyCore GC2385 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC2385 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc2385.
|
||||||
|
|
||||||
|
config VIDEO_GC5025
|
||||||
|
tristate "GalaxyCore GC5025 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC5025 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc5025.
|
||||||
|
|
||||||
|
config VIDEO_GC5035
|
||||||
|
tristate "GalaxyCore GC5035 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC5035 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc5035.
|
||||||
|
|
||||||
|
config VIDEO_GC8034
|
||||||
|
tristate "GalaxyCore GC8034 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the GalaxyCore GC8034 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called gc8034.
|
||||||
|
|
||||||
|
config VIDEO_BF3925
|
||||||
|
tristate "BYD BF3925 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the BYD BF3925 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called bf3925.
|
||||||
|
|
||||||
|
config VIDEO_JX_H65
|
||||||
|
tristate "SOI JX_H65 sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the SOI JX_H65 sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called jx_h65.
|
||||||
|
|
||||||
|
config VIDEO_SC031GS
|
||||||
|
tristate "SmartSens SC031GS sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the SmartSens SC031GS sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called sc031gs.
|
||||||
|
|
||||||
|
config VIDEO_SC132GS
|
||||||
|
tristate "SmartSens SC132GS sensor support"
|
||||||
|
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||||
|
depends on MEDIA_CAMERA_SUPPORT
|
||||||
|
select V4L2_FWNODE
|
||||||
|
help
|
||||||
|
Support for the SmartSens SC132GS sensor.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called sc132gs.
|
||||||
|
|
||||||
comment "Flash devices"
|
comment "Flash devices"
|
||||||
|
|
||||||
config VIDEO_ADP1653
|
config VIDEO_ADP1653
|
||||||
|
|||||||
@@ -67,9 +67,13 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
|
|||||||
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
|
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
|
||||||
obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
|
obj-$(CONFIG_VIDEO_OV2680) += ov2680.o
|
||||||
obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
|
obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV2718) += ov2718.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV2735) += ov2735.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
|
||||||
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
|
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
|
||||||
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
|
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
|
||||||
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
|
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV5648) += ov5648.o
|
||||||
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
|
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
|
||||||
obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
|
obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
|
||||||
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
|
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
|
||||||
@@ -78,7 +82,10 @@ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
|
|||||||
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
|
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
|
||||||
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
|
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
|
||||||
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
|
obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV8858) += ov8858.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV9281) += ov9281.o
|
||||||
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
|
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
|
||||||
|
obj-$(CONFIG_VIDEO_OV9750) += ov9750.o
|
||||||
obj-$(CONFIG_VIDEO_OV13850) += ov13850.o
|
obj-$(CONFIG_VIDEO_OV13850) += ov13850.o
|
||||||
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
|
obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
|
||||||
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
|
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
|
||||||
@@ -89,6 +96,7 @@ obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o
|
|||||||
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
|
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
|
||||||
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
|
obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
|
||||||
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
|
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
|
||||||
|
obj-$(CONFIG_VIDEO_AR0230) += ar0230.o
|
||||||
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
|
obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
|
||||||
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
|
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
|
||||||
obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o
|
obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o
|
||||||
@@ -107,9 +115,27 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
|
|||||||
obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
|
obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
|
||||||
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
|
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
|
||||||
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
|
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
|
||||||
|
obj-$(CONFIG_VIDEO_IMX219) += imx219.o
|
||||||
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
|
obj-$(CONFIG_VIDEO_IMX258) += imx258.o
|
||||||
|
obj-$(CONFIG_VIDEO_IMX258_EEPROM) += imx258_eeprom.o
|
||||||
obj-$(CONFIG_VIDEO_IMX274) += imx274.o
|
obj-$(CONFIG_VIDEO_IMX274) += imx274.o
|
||||||
|
obj-$(CONFIG_VIDEO_IMX317) += imx317.o
|
||||||
|
obj-$(CONFIG_VIDEO_IMX323) += imx323.o
|
||||||
|
obj-$(CONFIG_VIDEO_IMX327) += imx327.o
|
||||||
obj-$(CONFIG_VIDEO_GC0312) += gc0312.o
|
obj-$(CONFIG_VIDEO_GC0312) += gc0312.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC0329) += gc0329.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC0403) += gc0403.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC2035) += gc2035.o
|
||||||
obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
|
obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC2155) += gc2155.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC2355) += gc2355.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC2385) += gc2385.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC5025) += gc5025.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC5035) += gc5035.o
|
||||||
|
obj-$(CONFIG_VIDEO_GC8034) += gc8034.o
|
||||||
|
obj-$(CONFIG_VIDEO_BF3925) += bf3925.o
|
||||||
|
obj-$(CONFIG_VIDEO_JX_H65) += jx_h65.o
|
||||||
|
obj-$(CONFIG_VIDEO_SC031GS) += sc031gs.o
|
||||||
|
obj-$(CONFIG_VIDEO_SC132GS) += sc132gs.o
|
||||||
|
|
||||||
obj-$(CONFIG_SDR_MAX2175) += max2175.o
|
obj-$(CONFIG_SDR_MAX2175) += max2175.o
|
||||||
|
|||||||
1688
drivers/media/i2c/ar0230.c
Normal file
1688
drivers/media/i2c/ar0230.c
Normal file
File diff suppressed because it is too large
Load Diff
1498
drivers/media/i2c/bf3925.c
Normal file
1498
drivers/media/i2c/bf3925.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-common.h>
|
#include <media/v4l2-common.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include <media/v4l2-mediabus.h>
|
#include <media/v4l2-mediabus.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0)
|
||||||
#define DRIVER_NAME "gc0329"
|
#define DRIVER_NAME "gc0329"
|
||||||
#define GC0329_PIXEL_RATE (24 * 1000 * 1000)
|
#define GC0329_PIXEL_RATE (24 * 1000 * 1000)
|
||||||
|
|
||||||
@@ -96,6 +98,10 @@ struct gc0329 {
|
|||||||
struct v4l2_ctrl *link_frequency;
|
struct v4l2_ctrl *link_frequency;
|
||||||
const struct gc0329_framesize *frame_size;
|
const struct gc0329_framesize *frame_size;
|
||||||
int streaming;
|
int streaming;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sensor_register gc0329_vga_regs[] = {
|
static const struct sensor_register gc0329_vga_regs[] = {
|
||||||
@@ -652,6 +658,76 @@ static int gc0329_set_fmt(struct v4l2_subdev *sd,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gc0329_get_module_inf(struct gc0329 *gc0329,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, DRIVER_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, gc0329->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, gc0329->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gc0329_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct gc0329 *gc0329 = to_gc0329(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
gc0329_get_module_inf(gc0329, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long gc0329_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gc0329_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = gc0329_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int gc0329_s_stream(struct v4l2_subdev *sd, int on)
|
static int gc0329_s_stream(struct v4l2_subdev *sd, int on)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||||
@@ -803,6 +879,10 @@ static const struct v4l2_subdev_core_ops gc0329_subdev_core_ops = {
|
|||||||
.log_status = v4l2_ctrl_subdev_log_status,
|
.log_status = v4l2_ctrl_subdev_log_status,
|
||||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||||
|
.ioctl = gc0329_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = gc0329_compat_ioctl32,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops gc0329_subdev_video_ops = {
|
static const struct v4l2_subdev_video_ops gc0329_subdev_video_ops = {
|
||||||
@@ -942,14 +1022,35 @@ static int gc0329_parse_of(struct gc0329 *gc0329)
|
|||||||
static int gc0329_probe(struct i2c_client *client,
|
static int gc0329_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
struct gc0329 *gc0329;
|
struct gc0329 *gc0329;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
gc0329 = devm_kzalloc(&client->dev, sizeof(*gc0329), GFP_KERNEL);
|
gc0329 = devm_kzalloc(&client->dev, sizeof(*gc0329), GFP_KERNEL);
|
||||||
if (!gc0329)
|
if (!gc0329)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&gc0329->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&gc0329->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&gc0329->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&gc0329->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
gc0329->client = client;
|
gc0329->client = client;
|
||||||
gc0329->xvclk = devm_clk_get(&client->dev, "xvclk");
|
gc0329->xvclk = devm_clk_get(&client->dev, "xvclk");
|
||||||
if (IS_ERR(gc0329->xvclk)) {
|
if (IS_ERR(gc0329->xvclk)) {
|
||||||
@@ -995,8 +1096,8 @@ static int gc0329_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
gc0329->pad.flags = MEDIA_PAD_FL_SOURCE;
|
gc0329->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &gc0329->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &gc0329->pad);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
v4l2_ctrl_handler_free(&gc0329->ctrls);
|
v4l2_ctrl_handler_free(&gc0329->ctrls);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1015,7 +1116,16 @@ static int gc0329_probe(struct i2c_client *client,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(&gc0329->sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(gc0329->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
gc0329->module_index, facing,
|
||||||
|
DRIVER_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|||||||
1289
drivers/media/i2c/gc0403.c
Normal file
1289
drivers/media/i2c/gc0403.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-common.h>
|
#include <media/v4l2-common.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include <media/v4l2-mediabus.h>
|
#include <media/v4l2-mediabus.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0)
|
||||||
#define DRIVER_NAME "gc2035"
|
#define DRIVER_NAME "gc2035"
|
||||||
#define GC2035_PIXEL_RATE (70 * 1000 * 1000)
|
#define GC2035_PIXEL_RATE (70 * 1000 * 1000)
|
||||||
|
|
||||||
@@ -98,6 +100,10 @@ struct gc2035 {
|
|||||||
struct v4l2_ctrl *link_frequency;
|
struct v4l2_ctrl *link_frequency;
|
||||||
const struct gc2035_framesize *frame_size;
|
const struct gc2035_framesize *frame_size;
|
||||||
int streaming;
|
int streaming;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sensor_register gc2035_init_regs[] = {
|
static const struct sensor_register gc2035_init_regs[] = {
|
||||||
@@ -1190,10 +1196,84 @@ unlock:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gc2035_get_module_inf(struct gc2035 *gc2035,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, DRIVER_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, gc2035->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, gc2035->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gc2035_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct gc2035 *gc2035 = to_gc2035(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
gc2035_get_module_inf(gc2035, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long gc2035_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gc2035_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = gc2035_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct v4l2_subdev_core_ops gc2035_subdev_core_ops = {
|
static const struct v4l2_subdev_core_ops gc2035_subdev_core_ops = {
|
||||||
.log_status = v4l2_ctrl_subdev_log_status,
|
.log_status = v4l2_ctrl_subdev_log_status,
|
||||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||||
|
.ioctl = gc2035_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = gc2035_compat_ioctl32,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops gc2035_subdev_video_ops = {
|
static const struct v4l2_subdev_video_ops gc2035_subdev_video_ops = {
|
||||||
@@ -1334,14 +1414,35 @@ static int gc2035_parse_of(struct gc2035 *gc2035)
|
|||||||
static int gc2035_probe(struct i2c_client *client,
|
static int gc2035_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
struct gc2035 *gc2035;
|
struct gc2035 *gc2035;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
gc2035 = devm_kzalloc(&client->dev, sizeof(*gc2035), GFP_KERNEL);
|
gc2035 = devm_kzalloc(&client->dev, sizeof(*gc2035), GFP_KERNEL);
|
||||||
if (!gc2035)
|
if (!gc2035)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&gc2035->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&gc2035->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&gc2035->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&gc2035->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
gc2035->client = client;
|
gc2035->client = client;
|
||||||
gc2035->xvclk = devm_clk_get(&client->dev, "xvclk");
|
gc2035->xvclk = devm_clk_get(&client->dev, "xvclk");
|
||||||
if (IS_ERR(gc2035->xvclk)) {
|
if (IS_ERR(gc2035->xvclk)) {
|
||||||
@@ -1387,8 +1488,8 @@ static int gc2035_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
gc2035->pad.flags = MEDIA_PAD_FL_SOURCE;
|
gc2035->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &gc2035->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &gc2035->pad);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
v4l2_ctrl_handler_free(&gc2035->ctrls);
|
v4l2_ctrl_handler_free(&gc2035->ctrls);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1407,7 +1508,16 @@ static int gc2035_probe(struct i2c_client *client,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(&gc2035->sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(gc2035->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
gc2035->module_index, facing,
|
||||||
|
DRIVER_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* gc2155 driver
|
* gc2155 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -13,11 +16,16 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02)
|
||||||
|
|
||||||
#define REG_CHIP_ID_H 0xf0
|
#define REG_CHIP_ID_H 0xf0
|
||||||
#define REG_CHIP_ID_L 0xf1
|
#define REG_CHIP_ID_L 0xf1
|
||||||
#define CHIP_ID_H 0x21
|
#define CHIP_ID_H 0x21
|
||||||
@@ -27,6 +35,8 @@
|
|||||||
|
|
||||||
#define GC2155_XVCLK_FREQ 24000000
|
#define GC2155_XVCLK_FREQ 24000000
|
||||||
|
|
||||||
|
#define GC2155_NAME "gc2155"
|
||||||
|
|
||||||
static const char * const gc2155_supply_names[] = {
|
static const char * const gc2155_supply_names[] = {
|
||||||
"avdd",
|
"avdd",
|
||||||
"dovdd",
|
"dovdd",
|
||||||
@@ -54,11 +64,16 @@ struct gc2155 {
|
|||||||
struct regulator_bulk_data supplies[GC2155_NUM_SUPPLIES];
|
struct regulator_bulk_data supplies[GC2155_NUM_SUPPLIES];
|
||||||
|
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
struct mutex mutex; /* lock to serialize v4l2 callback */
|
struct mutex mutex; /* lock to serialize v4l2 callback */
|
||||||
struct v4l2_subdev subdev;
|
struct v4l2_subdev subdev;
|
||||||
struct media_pad pad;
|
struct media_pad pad;
|
||||||
|
|
||||||
const struct gc2155_mode *cur_mode;
|
const struct gc2155_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_gc2155(sd) container_of(sd, struct gc2155, subdev)
|
#define to_gc2155(sd) container_of(sd, struct gc2155, subdev)
|
||||||
@@ -1056,12 +1071,15 @@ static int __gc2155_power_on(struct gc2155 *gc2155)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_ERR(gc2155->xvclk)) {
|
ret = clk_set_rate(gc2155->xvclk, GC2155_XVCLK_FREQ);
|
||||||
ret = clk_prepare_enable(gc2155->xvclk);
|
if (ret < 0)
|
||||||
if (ret < 0) {
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
if (clk_get_rate(gc2155->xvclk) != GC2155_XVCLK_FREQ)
|
||||||
return ret;
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
}
|
ret = clk_prepare_enable(gc2155->xvclk);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_ERR(gc2155->pwdn_gpio))
|
if (!IS_ERR(gc2155->pwdn_gpio))
|
||||||
@@ -1086,6 +1104,76 @@ static void __gc2155_power_off(struct gc2155 *gc2155)
|
|||||||
regulator_bulk_disable(GC2155_NUM_SUPPLIES, gc2155->supplies);
|
regulator_bulk_disable(GC2155_NUM_SUPPLIES, gc2155->supplies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gc2155_get_module_inf(struct gc2155 *gc2155,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, GC2155_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, gc2155->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, gc2155->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gc2155_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct gc2155 *gc2155 = to_gc2155(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
gc2155_get_module_inf(gc2155, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long gc2155_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gc2155_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = gc2155_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int gc2155_s_stream(struct v4l2_subdev *sd, int on)
|
static int gc2155_s_stream(struct v4l2_subdev *sd, int on)
|
||||||
{
|
{
|
||||||
struct gc2155 *gc2155 = to_gc2155(sd);
|
struct gc2155 *gc2155 = to_gc2155(sd);
|
||||||
@@ -1105,12 +1193,6 @@ static int gc2155_s_stream(struct v4l2_subdev *sd, int on)
|
|||||||
goto unlock_and_return;
|
goto unlock_and_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gc2155_write_array(gc2155->client, gc2155_global_regs);
|
|
||||||
if (ret) {
|
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
goto unlock_and_return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = gc2155_write_array(gc2155->client,
|
ret = gc2155_write_array(gc2155->client,
|
||||||
gc2155->cur_mode->reg_list);
|
gc2155->cur_mode->reg_list);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -1130,6 +1212,44 @@ unlock_and_return:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int gc2155_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct gc2155 *gc2155 = to_gc2155(sd);
|
||||||
|
struct i2c_client *client = gc2155->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&gc2155->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (gc2155->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gc2155_write_array(gc2155->client, gc2155_global_regs);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(sd, "could not set init registers\n");
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gc2155->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
gc2155->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&gc2155->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
static int gc2155_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
static int gc2155_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||||
{
|
{
|
||||||
@@ -1177,6 +1297,14 @@ static const struct dev_pm_ops gc2155_pm_ops = {
|
|||||||
gc2155_runtime_resume, NULL)
|
gc2155_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops gc2155_core_ops = {
|
||||||
|
.s_power = gc2155_s_power,
|
||||||
|
.ioctl = gc2155_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = gc2155_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops gc2155_video_ops = {
|
static const struct v4l2_subdev_video_ops gc2155_video_ops = {
|
||||||
.s_stream = gc2155_s_stream,
|
.s_stream = gc2155_s_stream,
|
||||||
};
|
};
|
||||||
@@ -1189,6 +1317,7 @@ static const struct v4l2_subdev_pad_ops gc2155_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops gc2155_subdev_ops = {
|
static const struct v4l2_subdev_ops gc2155_subdev_ops = {
|
||||||
|
.core = &gc2155_core_ops,
|
||||||
.video = &gc2155_video_ops,
|
.video = &gc2155_video_ops,
|
||||||
.pad = &gc2155_pad_ops,
|
.pad = &gc2155_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -1235,13 +1364,34 @@ static int gc2155_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct gc2155 *gc2155;
|
struct gc2155 *gc2155;
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
gc2155 = devm_kzalloc(dev, sizeof(*gc2155), GFP_KERNEL);
|
gc2155 = devm_kzalloc(dev, sizeof(*gc2155), GFP_KERNEL);
|
||||||
if (!gc2155)
|
if (!gc2155)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&gc2155->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&gc2155->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&gc2155->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&gc2155->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
gc2155->client = client;
|
gc2155->client = client;
|
||||||
gc2155->cur_mode = &supported_modes[0];
|
gc2155->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -1250,13 +1400,6 @@ static int gc2155_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(gc2155->xvclk, GC2155_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(gc2155->xvclk) != GC2155_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
gc2155->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
gc2155->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(gc2155->reset_gpio))
|
if (IS_ERR(gc2155->reset_gpio))
|
||||||
@@ -1289,13 +1432,23 @@ static int gc2155_probe(struct i2c_client *client,
|
|||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
gc2155->pad.flags = MEDIA_PAD_FL_SOURCE;
|
gc2155->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
gc2155->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
gc2155->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&gc2155->subdev.entity, 1, &gc2155->pad, 0);
|
ret = media_entity_pads_init(&gc2155->subdev.entity, 1, &gc2155->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(&gc2155->subdev);
|
sd = &gc2155->subdev;
|
||||||
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(gc2155->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
gc2155->module_index, facing,
|
||||||
|
GC2155_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1353,7 +1506,7 @@ static const struct i2c_device_id gc2155_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver gc2155_i2c_driver = {
|
static struct i2c_driver gc2155_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gc2155",
|
.name = GC2155_NAME,
|
||||||
.pm = &gc2155_pm_ops,
|
.pm = &gc2155_pm_ops,
|
||||||
.of_match_table = of_match_ptr(gc2155_of_match),
|
.of_match_table = of_match_ptr(gc2155_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* gc2355 driver
|
* gc2355 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
#define DEBUG 1
|
#define DEBUG 1
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,12 +15,17 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -73,6 +79,8 @@
|
|||||||
#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default"
|
#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default"
|
||||||
#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep"
|
#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep"
|
||||||
|
|
||||||
|
#define GC2355_NAME "gc2355"
|
||||||
|
|
||||||
static const char * const gc2355_supply_names[] = {
|
static const char * const gc2355_supply_names[] = {
|
||||||
"avdd", /* Analog power */
|
"avdd", /* Analog power */
|
||||||
"dovdd", /* Digital I/O power */
|
"dovdd", /* Digital I/O power */
|
||||||
@@ -119,6 +127,10 @@ struct gc2355 {
|
|||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
const struct gc2355_mode *cur_mode;
|
const struct gc2355_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_gc2355(sd) container_of(sd, struct gc2355, subdev)
|
#define to_gc2355(sd) container_of(sd, struct gc2355, subdev)
|
||||||
@@ -509,6 +521,76 @@ static int gc2355_g_frame_interval(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gc2355_get_module_inf(struct gc2355 *gc2355,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, GC2355_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, gc2355->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, gc2355->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gc2355_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct gc2355 *gc2355 = to_gc2355(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
gc2355_get_module_inf(gc2355, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long gc2355_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gc2355_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = gc2355_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __gc2355_start_stream(struct gc2355 *gc2355)
|
static int __gc2355_start_stream(struct gc2355 *gc2355)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -601,13 +683,16 @@ static int __gc2355_power_on(struct gc2355 *gc2355)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(dev, "could not set pins\n");
|
dev_err(dev, "could not set pins\n");
|
||||||
}
|
}
|
||||||
|
ret = clk_set_rate(gc2355->xvclk, GC2355_XVCLK_FREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
if (clk_get_rate(gc2355->xvclk) != GC2355_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(gc2355->xvclk);
|
ret = clk_prepare_enable(gc2355->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_ERR(gc2355->reset_gpio))
|
if (!IS_ERR(gc2355->reset_gpio))
|
||||||
gpiod_set_value_cansleep(gc2355->reset_gpio, 0);
|
gpiod_set_value_cansleep(gc2355->reset_gpio, 0);
|
||||||
|
|
||||||
@@ -708,6 +793,13 @@ static const struct v4l2_subdev_internal_ops gc2355_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops gc2355_core_ops = {
|
||||||
|
.ioctl = gc2355_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = gc2355_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops gc2355_video_ops = {
|
static const struct v4l2_subdev_video_ops gc2355_video_ops = {
|
||||||
.s_stream = gc2355_s_stream,
|
.s_stream = gc2355_s_stream,
|
||||||
.g_frame_interval = gc2355_g_frame_interval,
|
.g_frame_interval = gc2355_g_frame_interval,
|
||||||
@@ -721,6 +813,7 @@ static const struct v4l2_subdev_pad_ops gc2355_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops gc2355_subdev_ops = {
|
static const struct v4l2_subdev_ops gc2355_subdev_ops = {
|
||||||
|
.core = &gc2355_core_ops,
|
||||||
.video = &gc2355_video_ops,
|
.video = &gc2355_video_ops,
|
||||||
.pad = &gc2355_pad_ops,
|
.pad = &gc2355_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -956,14 +1049,34 @@ static int gc2355_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct gc2355 *gc2355;
|
struct gc2355 *gc2355;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
gc2355 = devm_kzalloc(dev, sizeof(*gc2355), GFP_KERNEL);
|
gc2355 = devm_kzalloc(dev, sizeof(*gc2355), GFP_KERNEL);
|
||||||
if (!gc2355)
|
if (!gc2355)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&gc2355->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&gc2355->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&gc2355->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&gc2355->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
gc2355->client = client;
|
gc2355->client = client;
|
||||||
gc2355->cur_mode = &supported_modes[0];
|
gc2355->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -972,13 +1085,6 @@ static int gc2355_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(gc2355->xvclk, GC2355_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(gc2355->xvclk) != GC2355_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
gc2355->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
gc2355->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(gc2355->reset_gpio))
|
if (IS_ERR(gc2355->reset_gpio))
|
||||||
@@ -1029,17 +1135,27 @@ static int gc2355_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &gc2355_internal_ops;
|
sd->internal_ops = &gc2355_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
gc2355->pad.flags = MEDIA_PAD_FL_SOURCE;
|
gc2355->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &gc2355->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &gc2355->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(gc2355->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
gc2355->module_index, facing,
|
||||||
|
GC2355_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1100,7 +1216,7 @@ static const struct i2c_device_id gc2355_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver gc2355_i2c_driver = {
|
static struct i2c_driver gc2355_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gc2355",
|
.name = GC2355_NAME,
|
||||||
.pm = &gc2355_pm_ops,
|
.pm = &gc2355_pm_ops,
|
||||||
.of_match_table = of_match_ptr(gc2355_of_match),
|
.of_match_table = of_match_ptr(gc2355_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
1254
drivers/media/i2c/gc2385.c
Normal file
1254
drivers/media/i2c/gc2385.c
Normal file
File diff suppressed because it is too large
Load Diff
1922
drivers/media/i2c/gc5025.c
Normal file
1922
drivers/media/i2c/gc5025.c
Normal file
File diff suppressed because it is too large
Load Diff
1598
drivers/media/i2c/gc5035.c
Normal file
1598
drivers/media/i2c/gc5035.c
Normal file
File diff suppressed because it is too large
Load Diff
2165
drivers/media/i2c/gc8034.c
Normal file
2165
drivers/media/i2c/gc8034.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,12 +16,16 @@
|
|||||||
#include <linux/of_graph.h>
|
#include <linux/of_graph.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-device.h>
|
#include <media/v4l2-device.h>
|
||||||
#include <media/v4l2-fwnode.h>
|
#include <media/v4l2-fwnode.h>
|
||||||
#include <media/v4l2-image-sizes.h>
|
#include <media/v4l2-image-sizes.h>
|
||||||
#include <media/v4l2-mediabus.h>
|
#include <media/v4l2-mediabus.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0)
|
||||||
|
|
||||||
/* IMX219 supported geometry */
|
/* IMX219 supported geometry */
|
||||||
#define IMX219_TABLE_END 0xffff
|
#define IMX219_TABLE_END 0xffff
|
||||||
#define IMX219_ANALOGUE_GAIN_MULTIPLIER 256
|
#define IMX219_ANALOGUE_GAIN_MULTIPLIER 256
|
||||||
@@ -40,6 +44,8 @@
|
|||||||
|
|
||||||
#define IMX219_EXP_LINES_MARGIN 4
|
#define IMX219_EXP_LINES_MARGIN 4
|
||||||
|
|
||||||
|
#define IMX219_NAME "imx219"
|
||||||
|
|
||||||
static const s64 link_freq_menu_items[] = {
|
static const s64 link_freq_menu_items[] = {
|
||||||
456000000,
|
456000000,
|
||||||
};
|
};
|
||||||
@@ -231,6 +237,10 @@ struct imx219 {
|
|||||||
struct v4l2_ctrl *pixel_rate;
|
struct v4l2_ctrl *pixel_rate;
|
||||||
const struct imx219_mode *cur_mode;
|
const struct imx219_mode *cur_mode;
|
||||||
u16 cur_vts;
|
u16 cur_vts;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct imx219_mode supported_modes[] = {
|
static const struct imx219_mode supported_modes[] = {
|
||||||
@@ -711,6 +721,77 @@ static int imx219_get_fmt(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void imx219_get_module_inf(struct imx219 *imx219,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, IMX219_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, imx219->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, imx219->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long imx219_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||||
|
struct imx219 *imx219 = to_imx219(client);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
imx219_get_module_inf(imx219, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long imx219_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = imx219_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = imx219_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Various V4L2 operations tables */
|
/* Various V4L2 operations tables */
|
||||||
static struct v4l2_subdev_video_ops imx219_subdev_video_ops = {
|
static struct v4l2_subdev_video_ops imx219_subdev_video_ops = {
|
||||||
.s_stream = imx219_s_stream,
|
.s_stream = imx219_s_stream,
|
||||||
@@ -719,6 +800,10 @@ static struct v4l2_subdev_video_ops imx219_subdev_video_ops = {
|
|||||||
|
|
||||||
static struct v4l2_subdev_core_ops imx219_subdev_core_ops = {
|
static struct v4l2_subdev_core_ops imx219_subdev_core_ops = {
|
||||||
.s_power = imx219_s_power,
|
.s_power = imx219_s_power,
|
||||||
|
.ioctl = imx219_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = imx219_compat_ioctl32,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_pad_ops imx219_subdev_pad_ops = {
|
static const struct v4l2_subdev_pad_ops imx219_subdev_pad_ops = {
|
||||||
@@ -889,8 +974,17 @@ static int imx219_probe(struct i2c_client *client,
|
|||||||
{
|
{
|
||||||
struct imx219 *priv;
|
struct imx219 *priv;
|
||||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||||
dev_warn(&adapter->dev,
|
dev_warn(&adapter->dev,
|
||||||
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
|
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
|
||||||
@@ -900,6 +994,19 @@ static int imx219_probe(struct i2c_client *client,
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&priv->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&priv->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&priv->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&priv->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
priv->clk = devm_clk_get(&client->dev, NULL);
|
priv->clk = devm_clk_get(&client->dev, NULL);
|
||||||
if (IS_ERR(priv->clk)) {
|
if (IS_ERR(priv->clk)) {
|
||||||
dev_info(&client->dev, "Error %ld getting clock\n",
|
dev_info(&client->dev, "Error %ld getting clock\n",
|
||||||
@@ -923,14 +1030,26 @@ static int imx219_probe(struct i2c_client *client,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
|
|
||||||
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
|
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
priv->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&priv->subdev.entity, 1, &priv->pad, 0);
|
ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(&priv->subdev);
|
sd = &priv->subdev;
|
||||||
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(priv->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
priv->module_index, facing,
|
||||||
|
IMX219_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -963,7 +1082,7 @@ MODULE_DEVICE_TABLE(i2c, imx219_id);
|
|||||||
static struct i2c_driver imx219_i2c_driver = {
|
static struct i2c_driver imx219_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.of_match_table = of_match_ptr(imx219_of_match),
|
.of_match_table = of_match_ptr(imx219_of_match),
|
||||||
.name = "imx219",
|
.name = IMX219_NAME,
|
||||||
},
|
},
|
||||||
.probe = imx219_probe,
|
.probe = imx219_probe,
|
||||||
.remove = imx219_remove,
|
.remove = imx219_remove,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
377
drivers/media/i2c/imx258_eeprom.c
Normal file
377
drivers/media/i2c/imx258_eeprom.c
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
|
#include <media/v4l2-ctrls.h>
|
||||||
|
#include <media/v4l2-device.h>
|
||||||
|
#include "imx258_eeprom_head.h"
|
||||||
|
|
||||||
|
#define DEVICE_NAME "imx258_eeprom"
|
||||||
|
|
||||||
|
static inline struct imx258_eeprom_device
|
||||||
|
*sd_to_imx258_eeprom(struct v4l2_subdev *subdev)
|
||||||
|
{
|
||||||
|
return container_of(subdev, struct imx258_eeprom_device, sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read registers up to 4 at a time */
|
||||||
|
static int imx258_read_reg_otp(struct i2c_client *client, u16 reg,
|
||||||
|
unsigned int len, u32 *val)
|
||||||
|
{
|
||||||
|
struct i2c_msg msgs[2];
|
||||||
|
u8 *data_be_p;
|
||||||
|
__be32 data_be = 0;
|
||||||
|
__be16 reg_addr_be = cpu_to_be16(reg);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (len > 4 || !len)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data_be_p = (u8 *)&data_be;
|
||||||
|
/* Write register address */
|
||||||
|
msgs[0].addr = client->addr;
|
||||||
|
msgs[0].flags = 0;
|
||||||
|
msgs[0].len = 2;
|
||||||
|
msgs[0].buf = (u8 *)®_addr_be;
|
||||||
|
|
||||||
|
/* Read data from register */
|
||||||
|
msgs[1].addr = client->addr;
|
||||||
|
msgs[1].flags = I2C_M_RD;
|
||||||
|
msgs[1].len = len;
|
||||||
|
msgs[1].buf = &data_be_p[4 - len];
|
||||||
|
|
||||||
|
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
|
if (ret != ARRAY_SIZE(msgs))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
*val = be32_to_cpu(data_be);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 get_vendor_flag(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
u8 vendor_flag = 0;
|
||||||
|
|
||||||
|
if (client->addr == SLAVE_ADDRESS_GZ)
|
||||||
|
vendor_flag |= 0x80;
|
||||||
|
return vendor_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx258_otp_read_gz(struct imx258_eeprom_device *imx258_eeprom_dev)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = imx258_eeprom_dev->client;
|
||||||
|
int otp_flag, i;
|
||||||
|
struct imx258_otp_info *otp_ptr;
|
||||||
|
struct device *dev = &imx258_eeprom_dev->client->dev;
|
||||||
|
int ret = 0;
|
||||||
|
u32 r_value, gr_value, gb_value, b_value;
|
||||||
|
u32 temp = 0;
|
||||||
|
u32 checksum = 0;
|
||||||
|
|
||||||
|
otp_ptr = kzalloc(sizeof(*otp_ptr), GFP_KERNEL);
|
||||||
|
if (!otp_ptr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
otp_flag = 0;
|
||||||
|
/* OTP base information*/
|
||||||
|
ret = imx258_read_reg_otp(client, GZ_INFO_FLAG_REG,
|
||||||
|
1, &otp_flag);
|
||||||
|
if (otp_flag == 0x01) {
|
||||||
|
otp_ptr->flag = 0x80; /* valid INFO in OTP */
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_ID_REG,
|
||||||
|
1, &otp_ptr->module_id);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_LENS_ID_REG,
|
||||||
|
1, &otp_ptr->lens_id);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_PRODUCT_YEAR_REG,
|
||||||
|
1, &otp_ptr->year);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_PRODUCT_MONTH_REG,
|
||||||
|
1, &otp_ptr->month);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_PRODUCT_DAY_REG,
|
||||||
|
1, &otp_ptr->day);
|
||||||
|
dev_dbg(dev, "fac info: module(0x%x) lens(0x%x) time(%d_%d_%d)!\n",
|
||||||
|
otp_ptr->module_id,
|
||||||
|
otp_ptr->lens_id,
|
||||||
|
otp_ptr->year,
|
||||||
|
otp_ptr->month,
|
||||||
|
otp_ptr->day);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTP WB calibration data */
|
||||||
|
ret = imx258_read_reg_otp(client, GZ_AWB_FLAG_REG,
|
||||||
|
1, &otp_flag);
|
||||||
|
if (otp_flag == 0x01) {
|
||||||
|
otp_ptr->flag |= 0x40; /* valid AWB in OTP */
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_CUR_R_REG,
|
||||||
|
1, &r_value);
|
||||||
|
checksum += r_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_CUR_GR_REG,
|
||||||
|
1, &gr_value);
|
||||||
|
checksum += gr_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_CUR_GB_REG,
|
||||||
|
1, &gb_value);
|
||||||
|
checksum += gb_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_CUR_B_REG,
|
||||||
|
1, &b_value);
|
||||||
|
checksum += b_value;
|
||||||
|
otp_ptr->rg_ratio =
|
||||||
|
r_value * 1024 / ((gr_value + gb_value) / 2);
|
||||||
|
otp_ptr->bg_ratio =
|
||||||
|
b_value * 1024 / ((gr_value + gb_value) / 2);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_GOLDEN_R_REG,
|
||||||
|
1, &r_value);
|
||||||
|
checksum += r_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_GOLDEN_GR_REG,
|
||||||
|
1, &gr_value);
|
||||||
|
checksum += gr_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_GOLDEN_GB_REG,
|
||||||
|
1, &gb_value);
|
||||||
|
checksum += gb_value;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_GOLDEN_B_REG,
|
||||||
|
1, &b_value);
|
||||||
|
checksum += b_value;
|
||||||
|
otp_ptr->rg_golden =
|
||||||
|
r_value * 1024 / ((gr_value + gb_value) / 2);
|
||||||
|
otp_ptr->bg_golden =
|
||||||
|
b_value * 1024 / ((gr_value + gb_value) / 2);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_AWB_CHECKSUM_REG,
|
||||||
|
1, &temp);
|
||||||
|
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||||
|
dev_err(dev, "otp awb info: check sum (%d,%d),ret = %d !\n",
|
||||||
|
checksum,
|
||||||
|
temp,
|
||||||
|
ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
dev_dbg(dev, "awb cur:(rg 0x%x, bg 0x%x,)\n",
|
||||||
|
otp_ptr->rg_ratio, otp_ptr->bg_ratio);
|
||||||
|
dev_dbg(dev, "awb gol:(rg 0x%x, bg 0x%x)\n",
|
||||||
|
otp_ptr->rg_golden, otp_ptr->bg_golden);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
/* OTP LSC calibration data */
|
||||||
|
ret = imx258_read_reg_otp(client, GZ_LSC_FLAG_REG,
|
||||||
|
1, &otp_flag);
|
||||||
|
if (otp_flag == 0x01) {
|
||||||
|
otp_ptr->flag |= 0x10; /* valid LSC in OTP */
|
||||||
|
for (i = 0; i < 504; i++) {
|
||||||
|
ret |= imx258_read_reg_otp(client,
|
||||||
|
GZ_LSC_DATA_START_REG + i,
|
||||||
|
1, &temp);
|
||||||
|
otp_ptr->lenc[i] = temp;
|
||||||
|
checksum += temp;
|
||||||
|
dev_dbg(dev,
|
||||||
|
"otp read lsc addr = 0x%04x, lenc[%d] = %d\n",
|
||||||
|
GZ_LSC_DATA_START_REG + i, i, temp);
|
||||||
|
}
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_LSC_CHECKSUM_REG,
|
||||||
|
1, &temp);
|
||||||
|
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||||
|
dev_err(dev,
|
||||||
|
"otp lsc info: check sum (%d,%d),ret = %d !\n",
|
||||||
|
checksum, temp, ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
/* OTP VCM calibration data */
|
||||||
|
ret = imx258_read_reg_otp(client, GZ_VCM_FLAG_REG,
|
||||||
|
1, &otp_flag);
|
||||||
|
if (otp_flag == 0x01) {
|
||||||
|
otp_ptr->flag |= 0x20; /* valid VCM in OTP */
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_DIR_REG,
|
||||||
|
1, &otp_ptr->vcm_dir);
|
||||||
|
checksum += otp_ptr->vcm_dir;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_START_REG,
|
||||||
|
1, &temp);
|
||||||
|
checksum += temp;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_START_REG + 1,
|
||||||
|
1, &otp_ptr->vcm_start);
|
||||||
|
checksum += otp_ptr->vcm_start;
|
||||||
|
otp_ptr->vcm_start |= (temp << 8);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_END_REG,
|
||||||
|
1, &temp);
|
||||||
|
checksum += temp;
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_END_REG + 1,
|
||||||
|
1, &otp_ptr->vcm_end);
|
||||||
|
checksum += otp_ptr->vcm_end;
|
||||||
|
otp_ptr->vcm_end |= (temp << 8);
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_VCM_CHECKSUM_REG,
|
||||||
|
1, &temp);
|
||||||
|
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||||
|
dev_err(dev,
|
||||||
|
"otp VCM info: check sum (%d,%d),ret = %d !\n",
|
||||||
|
checksum, temp, ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
dev_dbg(dev, "vcm_info: 0x%x, 0x%x, 0x%x!\n",
|
||||||
|
otp_ptr->vcm_start,
|
||||||
|
otp_ptr->vcm_end,
|
||||||
|
otp_ptr->vcm_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
/* OTP SPC calibration data */
|
||||||
|
ret = imx258_read_reg_otp(client, GZ_SPC_FLAG_REG,
|
||||||
|
1, &otp_flag);
|
||||||
|
if (otp_flag == 0x01) {
|
||||||
|
otp_ptr->flag |= 0x08; /* valid LSC in OTP */
|
||||||
|
for (i = 0; i < 126; i++) {
|
||||||
|
ret |= imx258_read_reg_otp(client,
|
||||||
|
GZ_SPC_DATA_START_REG + i,
|
||||||
|
1, &temp);
|
||||||
|
otp_ptr->spc[i] = (uint8_t)temp;
|
||||||
|
checksum += temp;
|
||||||
|
dev_dbg(dev,
|
||||||
|
"otp read spc addr = 0x%04x, spc[%d] = %d\n",
|
||||||
|
GZ_SPC_DATA_START_REG + i, i, temp);
|
||||||
|
}
|
||||||
|
ret |= imx258_read_reg_otp(client, GZ_SPC_CHECKSUM_REG,
|
||||||
|
1, &temp);
|
||||||
|
if (ret != 0 || (checksum % 0xff) != temp) {
|
||||||
|
dev_err(dev,
|
||||||
|
"otp spc info: check sum (%d,%d),ret = %d !\n",
|
||||||
|
checksum, temp, ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otp_ptr->flag) {
|
||||||
|
imx258_eeprom_dev->otp = otp_ptr;
|
||||||
|
} else {
|
||||||
|
imx258_eeprom_dev->otp = NULL;
|
||||||
|
kfree(otp_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
imx258_eeprom_dev->otp = NULL;
|
||||||
|
kfree(otp_ptr);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx258_otp_read(struct imx258_eeprom_device *imx258_eeprom_dev)
|
||||||
|
{
|
||||||
|
u8 vendor_flag = 0;
|
||||||
|
struct i2c_client *client = imx258_eeprom_dev->client;
|
||||||
|
|
||||||
|
vendor_flag = get_vendor_flag(client);
|
||||||
|
if (vendor_flag == 0x80)
|
||||||
|
imx258_otp_read_gz(imx258_eeprom_dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long imx258_eeprom_ioctl(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct imx258_eeprom_device *imx258_eeprom_dev =
|
||||||
|
sd_to_imx258_eeprom(sd);
|
||||||
|
imx258_otp_read(imx258_eeprom_dev);
|
||||||
|
if (arg && imx258_eeprom_dev->otp)
|
||||||
|
memcpy(arg, imx258_eeprom_dev->otp,
|
||||||
|
sizeof(struct imx258_otp_info));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops imx258_eeprom_core_ops = {
|
||||||
|
.ioctl = imx258_eeprom_ioctl,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_ops imx258_eeprom_ops = {
|
||||||
|
.core = &imx258_eeprom_core_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void imx258_eeprom_subdev_cleanup(struct imx258_eeprom_device *dev)
|
||||||
|
{
|
||||||
|
v4l2_device_unregister_subdev(&dev->sd);
|
||||||
|
media_entity_cleanup(&dev->sd.entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx258_eeprom_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct imx258_eeprom_device *imx258_eeprom_dev;
|
||||||
|
|
||||||
|
dev_info(&client->dev, "probing...\n");
|
||||||
|
imx258_eeprom_dev = devm_kzalloc(&client->dev,
|
||||||
|
sizeof(*imx258_eeprom_dev),
|
||||||
|
GFP_KERNEL);
|
||||||
|
|
||||||
|
if (imx258_eeprom_dev == NULL) {
|
||||||
|
dev_err(&client->dev, "Probe failed\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
v4l2_i2c_subdev_init(&imx258_eeprom_dev->sd,
|
||||||
|
client, &imx258_eeprom_ops);
|
||||||
|
imx258_eeprom_dev->client = client;
|
||||||
|
pm_runtime_set_active(&client->dev);
|
||||||
|
pm_runtime_enable(&client->dev);
|
||||||
|
pm_runtime_idle(&client->dev);
|
||||||
|
|
||||||
|
dev_info(&client->dev, "probing successful\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx258_eeprom_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
|
struct imx258_eeprom_device *imx258_eeprom_dev =
|
||||||
|
sd_to_imx258_eeprom(sd);
|
||||||
|
kfree(imx258_eeprom_dev->otp);
|
||||||
|
pm_runtime_disable(&client->dev);
|
||||||
|
imx258_eeprom_subdev_cleanup(imx258_eeprom_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused imx258_eeprom_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused imx258_eeprom_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id imx258_eeprom_id_table[] = {
|
||||||
|
{ DEVICE_NAME, 0 },
|
||||||
|
{ { 0 } }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, imx258_eeprom_id_table);
|
||||||
|
|
||||||
|
static const struct of_device_id imx258_eeprom_of_table[] = {
|
||||||
|
{ .compatible = "sony,imx258_eeprom" },
|
||||||
|
{ { 0 } }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, imx258_eeprom_of_table);
|
||||||
|
|
||||||
|
static const struct dev_pm_ops imx258_eeprom_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(imx258_eeprom_suspend, imx258_eeprom_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(imx258_eeprom_suspend, imx258_eeprom_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_driver imx258_eeprom_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DEVICE_NAME,
|
||||||
|
.pm = &imx258_eeprom_pm_ops,
|
||||||
|
.of_match_table = imx258_eeprom_of_table,
|
||||||
|
},
|
||||||
|
.probe = &imx258_eeprom_probe,
|
||||||
|
.remove = &imx258_eeprom_remove,
|
||||||
|
.id_table = imx258_eeprom_id_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(imx258_eeprom_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("IMX258 OTP driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
62
drivers/media/i2c/imx258_eeprom_head.h
Normal file
62
drivers/media/i2c/imx258_eeprom_head.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/* Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd. */
|
||||||
|
|
||||||
|
#ifndef IMX258_EEPROM_HEAD_H
|
||||||
|
#define IMX258_EEPROM_HEAD_H
|
||||||
|
|
||||||
|
#define SLAVE_ADDRESS_GZ 0x50
|
||||||
|
#define GZ_INFO_FLAG_REG 0X0000
|
||||||
|
#define GZ_ID_REG 0X0005
|
||||||
|
#define GZ_LENS_ID_REG 0X0006
|
||||||
|
#define GZ_PRODUCT_YEAR_REG 0X000A
|
||||||
|
#define GZ_PRODUCT_MONTH_REG 0X000B
|
||||||
|
#define GZ_PRODUCT_DAY_REG 0X000C
|
||||||
|
#define GZ_AWB_FLAG_REG 0x001c
|
||||||
|
#define GZ_CUR_R_REG 0x001d
|
||||||
|
#define GZ_CUR_GR_REG 0x001e
|
||||||
|
#define GZ_CUR_GB_REG 0x001f
|
||||||
|
#define GZ_CUR_B_REG 0x0020
|
||||||
|
#define GZ_GOLDEN_R_REG 0x0021
|
||||||
|
#define GZ_GOLDEN_GR_REG 0x0022
|
||||||
|
#define GZ_GOLDEN_GB_REG 0x0023
|
||||||
|
#define GZ_GOLDEN_B_REG 0x0024
|
||||||
|
#define GZ_AWB_CHECKSUM_REG 0x0025
|
||||||
|
#define GZ_LSC_FLAG_REG 0X003A
|
||||||
|
#define GZ_LSC_DATA_START_REG 0x003B
|
||||||
|
#define GZ_LSC_CHECKSUM_REG 0x0233
|
||||||
|
#define GZ_VCM_FLAG_REG 0X0788
|
||||||
|
#define GZ_VCM_DIR_REG 0X0789
|
||||||
|
#define GZ_VCM_START_REG 0X078C
|
||||||
|
#define GZ_VCM_END_REG 0X078A
|
||||||
|
#define GZ_VCM_CHECKSUM_REG 0x0790
|
||||||
|
#define GZ_SPC_FLAG_REG 0X0CE1
|
||||||
|
#define GZ_SPC_DATA_START_REG 0x0CE2
|
||||||
|
#define GZ_SPC_CHECKSUM_REG 0x0d60
|
||||||
|
|
||||||
|
struct imx258_otp_info {
|
||||||
|
u32 flag; //bit[7]: info bit[6]:wb bit[5]:vcm bit[4]:lenc bit[3]:spc
|
||||||
|
u32 module_id;
|
||||||
|
u32 lens_id;
|
||||||
|
u32 year;
|
||||||
|
u32 month;
|
||||||
|
u32 day;
|
||||||
|
u32 rg_ratio;
|
||||||
|
u32 bg_ratio;
|
||||||
|
u32 rg_golden;
|
||||||
|
u32 bg_golden;
|
||||||
|
int vcm_start;
|
||||||
|
int vcm_end;
|
||||||
|
int vcm_dir;
|
||||||
|
u8 lenc[504];
|
||||||
|
u8 spc[126];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* imx258_eeprom device structure */
|
||||||
|
struct imx258_eeprom_device {
|
||||||
|
struct v4l2_subdev sd;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct imx258_otp_info *otp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* IMX258_EEPROM_HEAD_H */
|
||||||
|
|
||||||
1364
drivers/media/i2c/imx317.c
Normal file
1364
drivers/media/i2c/imx317.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
|||||||
* imx323 driver
|
* imx323 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -13,12 +15,17 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -60,6 +67,8 @@
|
|||||||
/* h_offs 35 v_offs 14 */
|
/* h_offs 35 v_offs 14 */
|
||||||
#define PIX_FORMAT MEDIA_BUS_FMT_SBGGR10_1X10
|
#define PIX_FORMAT MEDIA_BUS_FMT_SBGGR10_1X10
|
||||||
|
|
||||||
|
#define IMX323_NAME "imx323"
|
||||||
|
|
||||||
struct cam_regulator {
|
struct cam_regulator {
|
||||||
char name[32];
|
char name[32];
|
||||||
int val;
|
int val;
|
||||||
@@ -106,7 +115,12 @@ struct imx323 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct imx323_mode *cur_mode;
|
const struct imx323_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_imx323(sd) container_of(sd, struct imx323, subdev)
|
#define to_imx323(sd) container_of(sd, struct imx323, subdev)
|
||||||
@@ -370,6 +384,76 @@ static int imx323_enable_test_pattern(struct imx323 *imx323, u32 pattern)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void imx323_get_module_inf(struct imx323 *imx323,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, IMX323_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, imx323->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, imx323->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long imx323_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct imx323 *imx323 = to_imx323(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
imx323_get_module_inf(imx323, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long imx323_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = imx323_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = imx323_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __imx323_start_stream(struct imx323 *imx323)
|
static int __imx323_start_stream(struct imx323 *imx323)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@@ -445,6 +529,37 @@ static int imx323_g_frame_interval(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imx323_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct imx323 *imx323 = to_imx323(sd);
|
||||||
|
struct i2c_client *client = imx323->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&imx323->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (imx323->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
imx323->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
imx323->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&imx323->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 imx323_cal_delay(u32 cycles)
|
static inline u32 imx323_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -576,6 +691,14 @@ static const struct v4l2_subdev_internal_ops imx323_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops imx323_core_ops = {
|
||||||
|
.s_power = imx323_s_power,
|
||||||
|
.ioctl = imx323_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = imx323_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops imx323_video_ops = {
|
static const struct v4l2_subdev_video_ops imx323_video_ops = {
|
||||||
.s_stream = imx323_s_stream,
|
.s_stream = imx323_s_stream,
|
||||||
.g_mbus_config = imx323_g_mbus_config,
|
.g_mbus_config = imx323_g_mbus_config,
|
||||||
@@ -590,6 +713,7 @@ static const struct v4l2_subdev_pad_ops imx323_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops imx323_subdev_ops = {
|
static const struct v4l2_subdev_ops imx323_subdev_ops = {
|
||||||
|
.core = &imx323_core_ops,
|
||||||
.video = &imx323_video_ops,
|
.video = &imx323_video_ops,
|
||||||
.pad = &imx323_pad_ops,
|
.pad = &imx323_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -710,7 +834,7 @@ static int imx323_check_sensor_id(struct imx323 *imx323,
|
|||||||
IMX323_REG_VALUE_08BIT, &id);
|
IMX323_REG_VALUE_08BIT, &id);
|
||||||
if (id != CHIP_ID) {
|
if (id != CHIP_ID) {
|
||||||
dev_err(dev, "Unexpected sensor id(%x), ret(%d)\n", id, ret);
|
dev_err(dev, "Unexpected sensor id(%x), ret(%d)\n", id, ret);
|
||||||
return ret;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "Detected IMX323 sensor\n");
|
dev_info(dev, "Detected IMX323 sensor\n");
|
||||||
@@ -735,14 +859,34 @@ static int imx323_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct imx323 *imx323;
|
struct imx323 *imx323;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
imx323 = devm_kzalloc(dev, sizeof(*imx323), GFP_KERNEL);
|
imx323 = devm_kzalloc(dev, sizeof(*imx323), GFP_KERNEL);
|
||||||
if (!imx323)
|
if (!imx323)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&imx323->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&imx323->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&imx323->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&imx323->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
imx323->client = client;
|
imx323->client = client;
|
||||||
imx323->cur_mode = &supported_modes[0];
|
imx323->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -784,17 +928,27 @@ static int imx323_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &imx323_internal_ops;
|
sd->internal_ops = &imx323_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
imx323->pad.flags = MEDIA_PAD_FL_SOURCE;
|
imx323->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &imx323->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &imx323->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(imx323->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
imx323->module_index, facing,
|
||||||
|
IMX323_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -855,7 +1009,7 @@ static const struct i2c_device_id imx323_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver imx323_i2c_driver = {
|
static struct i2c_driver imx323_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "imx323",
|
.name = IMX323_NAME,
|
||||||
.pm = &imx323_pm_ops,
|
.pm = &imx323_pm_ops,
|
||||||
.of_match_table = of_match_ptr(imx323_of_match),
|
.of_match_table = of_match_ptr(imx323_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
1386
drivers/media/i2c/imx327.c
Normal file
1386
drivers/media/i2c/imx327.c
Normal file
File diff suppressed because it is too large
Load Diff
1246
drivers/media/i2c/jx_h65.c
Normal file
1246
drivers/media/i2c/jx_h65.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,12 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
|
||||||
/*
|
/*
|
||||||
* ov2685 driver
|
* ov2685 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,16 +18,19 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0)
|
||||||
|
|
||||||
#define CHIP_ID 0x2685
|
#define CHIP_ID 0x2685
|
||||||
#define OV2685_REG_CHIP_ID 0x300a
|
#define OV2685_REG_CHIP_ID 0x300a
|
||||||
|
|
||||||
#define OV2685_XVCLK_FREQ 24000000
|
|
||||||
|
|
||||||
#define REG_SC_CTRL_MODE 0x0100
|
#define REG_SC_CTRL_MODE 0x0100
|
||||||
#define SC_CTRL_MODE_STANDBY 0x0
|
#define SC_CTRL_MODE_STANDBY 0x0
|
||||||
#define SC_CTRL_MODE_STREAMING BIT(0)
|
#define SC_CTRL_MODE_STREAMING BIT(0)
|
||||||
@@ -44,7 +51,7 @@
|
|||||||
#define OV2685_REG_TEST_PATTERN 0x5080
|
#define OV2685_REG_TEST_PATTERN 0x5080
|
||||||
#define OV2685_TEST_PATTERN_DISABLED 0x00
|
#define OV2685_TEST_PATTERN_DISABLED 0x00
|
||||||
#define OV2685_TEST_PATTERN_COLOR_BAR 0x80
|
#define OV2685_TEST_PATTERN_COLOR_BAR 0x80
|
||||||
#define OV2685_TEST_PATTERN_RANDOM 0x81
|
#define OV2685_TEST_PATTERN_RND 0x81
|
||||||
#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88
|
#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88
|
||||||
#define OV2685_TEST_PATTERN_BW_SQUARE 0x92
|
#define OV2685_TEST_PATTERN_BW_SQUARE 0x92
|
||||||
#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82
|
#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82
|
||||||
@@ -58,13 +65,7 @@
|
|||||||
#define OV2685_LANES 1
|
#define OV2685_LANES 1
|
||||||
#define OV2685_BITS_PER_SAMPLE 10
|
#define OV2685_BITS_PER_SAMPLE 10
|
||||||
|
|
||||||
static const char * const ov2685_supply_names[] = {
|
#define OV2685_NAME "ov2685"
|
||||||
"avdd", /* Analog power */
|
|
||||||
"dovdd", /* Digital I/O power */
|
|
||||||
"dvdd", /* Digital core power */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define OV2685_NUM_SUPPLIES ARRAY_SIZE(ov2685_supply_names)
|
|
||||||
|
|
||||||
struct regval {
|
struct regval {
|
||||||
u16 addr;
|
u16 addr;
|
||||||
@@ -83,8 +84,10 @@ struct ov2685_mode {
|
|||||||
struct ov2685 {
|
struct ov2685 {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct clk *xvclk;
|
struct clk *xvclk;
|
||||||
|
struct regulator *avdd_regulator; /* Analog power */
|
||||||
|
struct regulator *dovdd_regulator; /* Digital I/O power */
|
||||||
|
/* use internal DVDD power */
|
||||||
struct gpio_desc *reset_gpio;
|
struct gpio_desc *reset_gpio;
|
||||||
struct regulator_bulk_data supplies[OV2685_NUM_SUPPLIES];
|
|
||||||
|
|
||||||
bool streaming;
|
bool streaming;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
@@ -98,8 +101,11 @@ struct ov2685 {
|
|||||||
struct v4l2_ctrl_handler ctrl_handler;
|
struct v4l2_ctrl_handler ctrl_handler;
|
||||||
|
|
||||||
const struct ov2685_mode *cur_mode;
|
const struct ov2685_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
|
#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
|
||||||
|
|
||||||
/* PLL settings bases on 24M xvclk */
|
/* PLL settings bases on 24M xvclk */
|
||||||
@@ -119,7 +125,7 @@ static struct regval ov2685_1600x1200_regs[] = {
|
|||||||
{0x3087, 0x00},
|
{0x3087, 0x00},
|
||||||
{0x3501, 0x4e},
|
{0x3501, 0x4e},
|
||||||
{0x3502, 0xe0},
|
{0x3502, 0xe0},
|
||||||
{0x3503, 0x27},
|
{0x3503, 0x07},
|
||||||
{0x350b, 0x36},
|
{0x350b, 0x36},
|
||||||
{0x3600, 0xb4},
|
{0x3600, 0xb4},
|
||||||
{0x3603, 0x35},
|
{0x3603, 0x35},
|
||||||
@@ -215,17 +221,17 @@ static const s64 link_freq_menu_items[] = {
|
|||||||
static const char * const ov2685_test_pattern_menu[] = {
|
static const char * const ov2685_test_pattern_menu[] = {
|
||||||
"Disabled",
|
"Disabled",
|
||||||
"Color Bar",
|
"Color Bar",
|
||||||
|
"RND PATTERN",
|
||||||
"Color Bar FADE",
|
"Color Bar FADE",
|
||||||
"Random Data",
|
"BW SQUARE",
|
||||||
"Black White Square",
|
"COLOR SQUARE"
|
||||||
"Color Square"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int ov2685_test_pattern_val[] = {
|
static const int ov2685_test_pattern_val[] = {
|
||||||
OV2685_TEST_PATTERN_DISABLED,
|
OV2685_TEST_PATTERN_DISABLED,
|
||||||
OV2685_TEST_PATTERN_COLOR_BAR,
|
OV2685_TEST_PATTERN_COLOR_BAR,
|
||||||
|
OV2685_TEST_PATTERN_RND,
|
||||||
OV2685_TEST_PATTERN_COLOR_BAR_FADE,
|
OV2685_TEST_PATTERN_COLOR_BAR_FADE,
|
||||||
OV2685_TEST_PATTERN_RANDOM,
|
|
||||||
OV2685_TEST_PATTERN_BW_SQUARE,
|
OV2685_TEST_PATTERN_BW_SQUARE,
|
||||||
OV2685_TEST_PATTERN_COLOR_SQUARE,
|
OV2685_TEST_PATTERN_COLOR_SQUARE,
|
||||||
};
|
};
|
||||||
@@ -243,9 +249,10 @@ static const struct ov2685_mode supported_modes[] = {
|
|||||||
|
|
||||||
/* Write registers up to 4 at a time */
|
/* Write registers up to 4 at a time */
|
||||||
static int ov2685_write_reg(struct i2c_client *client, u16 reg,
|
static int ov2685_write_reg(struct i2c_client *client, u16 reg,
|
||||||
u32 len, u32 val)
|
unsigned int len, u32 val)
|
||||||
{
|
{
|
||||||
u32 val_i, buf_i;
|
int buf_i;
|
||||||
|
int val_i;
|
||||||
u8 buf[6];
|
u8 buf[6];
|
||||||
u8 *val_p;
|
u8 *val_p;
|
||||||
__be32 val_be;
|
__be32 val_be;
|
||||||
@@ -273,8 +280,7 @@ static int ov2685_write_reg(struct i2c_client *client, u16 reg,
|
|||||||
static int ov2685_write_array(struct i2c_client *client,
|
static int ov2685_write_array(struct i2c_client *client,
|
||||||
const struct regval *regs)
|
const struct regval *regs)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int i, ret = 0;
|
||||||
u32 i;
|
|
||||||
|
|
||||||
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
|
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
|
||||||
ret = ov2685_write_reg(client, regs[i].addr,
|
ret = ov2685_write_reg(client, regs[i].addr,
|
||||||
@@ -285,7 +291,7 @@ static int ov2685_write_array(struct i2c_client *client,
|
|||||||
|
|
||||||
/* Read registers up to 4 at a time */
|
/* Read registers up to 4 at a time */
|
||||||
static int ov2685_read_reg(struct i2c_client *client, u16 reg,
|
static int ov2685_read_reg(struct i2c_client *client, u16 reg,
|
||||||
u32 len, u32 *val)
|
unsigned int len, u32 *val)
|
||||||
{
|
{
|
||||||
struct i2c_msg msgs[2];
|
struct i2c_msg msgs[2];
|
||||||
u8 *data_be_p;
|
u8 *data_be_p;
|
||||||
@@ -318,12 +324,12 @@ static int ov2685_read_reg(struct i2c_client *client, u16 reg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ov2685_fill_fmt(const struct ov2685_mode *mode,
|
static void ov2685_fill_fmt(struct ov2685 *ov2685,
|
||||||
struct v4l2_mbus_framefmt *fmt)
|
struct v4l2_mbus_framefmt *fmt)
|
||||||
{
|
{
|
||||||
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
||||||
fmt->width = mode->width;
|
fmt->width = ov2685->cur_mode->width;
|
||||||
fmt->height = mode->height;
|
fmt->height = ov2685->cur_mode->height;
|
||||||
fmt->field = V4L2_FIELD_NONE;
|
fmt->field = V4L2_FIELD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,8 +340,7 @@ static int ov2685_set_fmt(struct v4l2_subdev *sd,
|
|||||||
struct ov2685 *ov2685 = to_ov2685(sd);
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
|
struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
|
||||||
|
|
||||||
/* only one mode supported for now */
|
ov2685_fill_fmt(ov2685, mbus_fmt);
|
||||||
ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -347,7 +352,7 @@ static int ov2685_get_fmt(struct v4l2_subdev *sd,
|
|||||||
struct ov2685 *ov2685 = to_ov2685(sd);
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
|
struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
|
||||||
|
|
||||||
ov2685_fill_fmt(ov2685->cur_mode, mbus_fmt);
|
ov2685_fill_fmt(ov2685, mbus_fmt);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -360,7 +365,6 @@ static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,16 +387,34 @@ static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
|
||||||
static inline u32 ov2685_cal_delay(u32 cycles)
|
|
||||||
{
|
{
|
||||||
return DIV_ROUND_UP(cycles, OV2685_XVCLK_FREQ / 1000 / 1000);
|
ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
|
||||||
|
OV2685_REG_VALUE_24BIT, val << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
|
||||||
|
{
|
||||||
|
ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
|
||||||
|
OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
|
||||||
|
{
|
||||||
|
val += ov2685->cur_mode->height;
|
||||||
|
ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
|
||||||
|
OV2685_REG_VALUE_16BIT, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
|
||||||
|
{
|
||||||
|
ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
|
||||||
|
OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __ov2685_power_on(struct ov2685 *ov2685)
|
static int __ov2685_power_on(struct ov2685 *ov2685)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u32 delay_us;
|
|
||||||
struct device *dev = &ov2685->client->dev;
|
struct device *dev = &ov2685->client->dev;
|
||||||
|
|
||||||
ret = clk_prepare_enable(ov2685->xvclk);
|
ret = clk_prepare_enable(ov2685->xvclk);
|
||||||
@@ -400,33 +422,37 @@ static int __ov2685_power_on(struct ov2685 *ov2685)
|
|||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
clk_set_rate(ov2685->xvclk, 24000000);
|
||||||
|
|
||||||
gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
|
gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
|
||||||
|
/* AVDD and DOVDD may rise in any order */
|
||||||
ret = regulator_bulk_enable(OV2685_NUM_SUPPLIES, ov2685->supplies);
|
ret = regulator_enable(ov2685->avdd_regulator);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable regulators\n");
|
dev_err(dev, "Failed to enable AVDD regulator\n");
|
||||||
goto disable_clk;
|
goto disable_xvclk;
|
||||||
}
|
}
|
||||||
|
ret = regulator_enable(ov2685->dovdd_regulator);
|
||||||
/* The minimum delay between power supplies and reset rising can be 0 */
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to enable DOVDD regulator\n");
|
||||||
|
goto disable_avdd;
|
||||||
|
}
|
||||||
|
/* The minimum delay between AVDD and reset rising can be 0 */
|
||||||
gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
|
gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
|
||||||
/* 8192 xvclk cycles prior to the first SCCB transaction */
|
/* 8192 xvclk cycles prior to the first SCCB transaction.
|
||||||
delay_us = ov2685_cal_delay(8192);
|
* NOTE: An additional 1ms must be added to wait for
|
||||||
usleep_range(delay_us, delay_us * 2);
|
* SCCB to become stable when using internal DVDD.
|
||||||
|
*/
|
||||||
|
usleep_range(1350, 1500);
|
||||||
|
|
||||||
/* HACK: ov2685 would output messy data after reset(R0103),
|
/* HACK: ov2685 would output messy data after reset(R0103),
|
||||||
* writing register before .s_stream() as a workaround
|
* writing register before .s_stream() as a workaround
|
||||||
*/
|
*/
|
||||||
ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
|
ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
|
||||||
if (ret)
|
|
||||||
goto disable_supplies;
|
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
|
disable_avdd:
|
||||||
disable_supplies:
|
regulator_disable(ov2685->avdd_regulator);
|
||||||
regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies);
|
disable_xvclk:
|
||||||
disable_clk:
|
|
||||||
clk_disable_unprepare(ov2685->xvclk);
|
clk_disable_unprepare(ov2685->xvclk);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -435,14 +461,100 @@ disable_clk:
|
|||||||
static void __ov2685_power_off(struct ov2685 *ov2685)
|
static void __ov2685_power_off(struct ov2685 *ov2685)
|
||||||
{
|
{
|
||||||
/* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
|
/* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
|
||||||
u32 delay_us = ov2685_cal_delay(512);
|
usleep_range(30, 50);
|
||||||
|
|
||||||
usleep_range(delay_us, delay_us * 2);
|
|
||||||
clk_disable_unprepare(ov2685->xvclk);
|
clk_disable_unprepare(ov2685->xvclk);
|
||||||
gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
|
gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
|
||||||
regulator_bulk_disable(OV2685_NUM_SUPPLIES, ov2685->supplies);
|
regulator_disable(ov2685->dovdd_regulator);
|
||||||
|
regulator_disable(ov2685->avdd_regulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov2685_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov2685->mutex);
|
||||||
|
|
||||||
|
if (on)
|
||||||
|
ret = pm_runtime_get_sync(&ov2685->client->dev);
|
||||||
|
else
|
||||||
|
ret = pm_runtime_put(&ov2685->client->dev);
|
||||||
|
|
||||||
|
mutex_unlock(&ov2685->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ov2685_get_module_inf(struct ov2685 *ov2685,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV2685_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov2685->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov2685->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ov2685_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov2685_get_module_inf(ov2685, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov2685_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov2685_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov2685_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
|
static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
|
||||||
{
|
{
|
||||||
struct ov2685 *ov2685 = to_ov2685(sd);
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
@@ -456,33 +568,27 @@ static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
|
|||||||
goto unlock_and_return;
|
goto unlock_and_return;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
ret = pm_runtime_get_sync(&ov2685->client->dev);
|
/* In case these controls are set before streaming */
|
||||||
if (ret < 0) {
|
ov2685_set_exposure(ov2685, ov2685->exposure->val);
|
||||||
pm_runtime_put_noidle(&client->dev);
|
ov2685_set_gain(ov2685, ov2685->anal_gain->val);
|
||||||
goto unlock_and_return;
|
ov2685_set_vts(ov2685, ov2685->vblank->val);
|
||||||
}
|
ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
|
||||||
ret = __v4l2_ctrl_handler_setup(&ov2685->ctrl_handler);
|
|
||||||
if (ret) {
|
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
goto unlock_and_return;
|
|
||||||
}
|
|
||||||
ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
|
ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
|
||||||
OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
|
OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
|
||||||
if (ret) {
|
if (ret)
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
goto unlock_and_return;
|
goto unlock_and_return;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ov2685_write_reg(client, REG_SC_CTRL_MODE,
|
ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
|
||||||
OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
|
OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
|
||||||
pm_runtime_put(&ov2685->client->dev);
|
if (ret)
|
||||||
|
goto unlock_and_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ov2685->streaming = on;
|
ov2685->streaming = on;
|
||||||
|
|
||||||
unlock_and_return:
|
unlock_and_return:
|
||||||
mutex_unlock(&ov2685->mutex);
|
mutex_unlock(&ov2685->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +602,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|||||||
|
|
||||||
try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
|
try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
|
||||||
/* Initialize try_fmt */
|
/* Initialize try_fmt */
|
||||||
ov2685_fill_fmt(&supported_modes[0], try_fmt);
|
ov2685_fill_fmt(ov2685, try_fmt);
|
||||||
|
|
||||||
mutex_unlock(&ov2685->mutex);
|
mutex_unlock(&ov2685->mutex);
|
||||||
|
|
||||||
@@ -504,16 +610,27 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int __maybe_unused ov2685_runtime_resume(struct device *dev)
|
static int ov2685_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
struct ov2685 *ov2685 = to_ov2685(sd);
|
struct ov2685 *ov2685 = to_ov2685(sd);
|
||||||
|
int ret;
|
||||||
|
|
||||||
return __ov2685_power_on(ov2685);
|
ret = __ov2685_power_on(ov2685);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ov2685->streaming) {
|
||||||
|
ret = ov2685_s_stream(sd, 1);
|
||||||
|
if (ret)
|
||||||
|
__ov2685_power_off(ov2685);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused ov2685_runtime_suspend(struct device *dev)
|
static int ov2685_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
@@ -534,67 +651,66 @@ static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
struct ov2685 *ov2685 = container_of(ctrl->handler,
|
struct ov2685 *ov2685 = container_of(ctrl->handler,
|
||||||
struct ov2685, ctrl_handler);
|
struct ov2685, ctrl_handler);
|
||||||
struct i2c_client *client = ov2685->client;
|
struct i2c_client *client = ov2685->client;
|
||||||
s64 max_expo;
|
s64 max;
|
||||||
int ret;
|
int ret = 0;
|
||||||
|
|
||||||
/* Propagate change of current control to all related controls */
|
/* Propagate change of current control to all related controls */
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
case V4L2_CID_VBLANK:
|
case V4L2_CID_VBLANK:
|
||||||
/* Update max exposure while meeting expected vblanking */
|
/* Update max exposure while meeting expected vblanking */
|
||||||
max_expo = ov2685->cur_mode->height + ctrl->val - 4;
|
max = ov2685->cur_mode->height + ctrl->val - 4;
|
||||||
__v4l2_ctrl_modify_range(ov2685->exposure,
|
__v4l2_ctrl_modify_range(ov2685->exposure,
|
||||||
ov2685->exposure->minimum, max_expo,
|
ov2685->exposure->minimum, max,
|
||||||
ov2685->exposure->step,
|
ov2685->exposure->step,
|
||||||
ov2685->exposure->default_value);
|
ov2685->exposure->default_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pm_runtime_get_if_in_use(&client->dev) <= 0)
|
pm_runtime_get_sync(&client->dev);
|
||||||
return 0;
|
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
case V4L2_CID_EXPOSURE:
|
case V4L2_CID_EXPOSURE:
|
||||||
ret = ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
|
ov2685_set_exposure(ov2685, ctrl->val);
|
||||||
OV2685_REG_VALUE_24BIT, ctrl->val << 4);
|
|
||||||
break;
|
break;
|
||||||
case V4L2_CID_ANALOGUE_GAIN:
|
case V4L2_CID_ANALOGUE_GAIN:
|
||||||
ret = ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
|
ov2685_set_gain(ov2685, ctrl->val);
|
||||||
OV2685_REG_VALUE_16BIT, ctrl->val);
|
|
||||||
break;
|
break;
|
||||||
case V4L2_CID_VBLANK:
|
case V4L2_CID_VBLANK:
|
||||||
ret = ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
|
ov2685_set_vts(ov2685, ctrl->val);
|
||||||
OV2685_REG_VALUE_16BIT,
|
|
||||||
ctrl->val + ov2685->cur_mode->height);
|
|
||||||
break;
|
break;
|
||||||
case V4L2_CID_TEST_PATTERN:
|
case V4L2_CID_TEST_PATTERN:
|
||||||
ret = ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
|
ov2685_enable_test_pattern(ov2685, ctrl->val);
|
||||||
OV2685_REG_VALUE_08BIT,
|
|
||||||
ov2685_test_pattern_val[ctrl->val]);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
||||||
__func__, ctrl->id, ctrl->val);
|
__func__, ctrl->id, ctrl->val);
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
pm_runtime_put(&client->dev);
|
pm_runtime_put(&client->dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops ov2685_video_ops = {
|
static struct v4l2_subdev_core_ops ov2685_core_ops = {
|
||||||
|
.s_power = ov2685_s_power,
|
||||||
|
.ioctl = ov2685_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov2685_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct v4l2_subdev_video_ops ov2685_video_ops = {
|
||||||
.s_stream = ov2685_s_stream,
|
.s_stream = ov2685_s_stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_pad_ops ov2685_pad_ops = {
|
static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
|
||||||
.enum_mbus_code = ov2685_enum_mbus_code,
|
.enum_mbus_code = ov2685_enum_mbus_code,
|
||||||
.enum_frame_size = ov2685_enum_frame_sizes,
|
.enum_frame_size = ov2685_enum_frame_sizes,
|
||||||
.get_fmt = ov2685_get_fmt,
|
.get_fmt = ov2685_get_fmt,
|
||||||
.set_fmt = ov2685_set_fmt,
|
.set_fmt = ov2685_set_fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov2685_subdev_ops = {
|
static struct v4l2_subdev_ops ov2685_subdev_ops = {
|
||||||
|
.core = &ov2685_core_ops,
|
||||||
.video = &ov2685_video_ops,
|
.video = &ov2685_video_ops,
|
||||||
.pad = &ov2685_pad_ops,
|
.pad = &ov2685_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -620,7 +736,7 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685)
|
|||||||
|
|
||||||
handler = &ov2685->ctrl_handler;
|
handler = &ov2685->ctrl_handler;
|
||||||
mode = ov2685->cur_mode;
|
mode = ov2685->cur_mode;
|
||||||
ret = v4l2_ctrl_handler_init(handler, 8);
|
ret = v4l2_ctrl_handler_init(handler, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
handler->lock = &ov2685->mutex;
|
handler->lock = &ov2685->mutex;
|
||||||
@@ -663,34 +779,31 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685)
|
|||||||
0, 0, ov2685_test_pattern_menu);
|
0, 0, ov2685_test_pattern_menu);
|
||||||
|
|
||||||
if (handler->error) {
|
if (handler->error) {
|
||||||
ret = handler->error;
|
v4l2_ctrl_handler_free(handler);
|
||||||
dev_err(&ov2685->client->dev,
|
return handler->error;
|
||||||
"Failed to init controls(%d)\n", ret);
|
|
||||||
goto err_free_handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ov2685->subdev.ctrl_handler = handler;
|
ov2685->subdev.ctrl_handler = handler;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_handler:
|
|
||||||
v4l2_ctrl_handler_free(handler);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ov2685_check_sensor_id(struct ov2685 *ov2685,
|
static int ov2685_check_sensor_id(struct ov2685 *ov2685,
|
||||||
struct i2c_client *client)
|
struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct device *dev = &ov2685->client->dev;
|
struct device *dev = &ov2685->client->dev;
|
||||||
int ret;
|
int id, ret;
|
||||||
u32 id = 0;
|
|
||||||
|
|
||||||
ret = ov2685_read_reg(client, OV2685_REG_CHIP_ID,
|
ret = __ov2685_power_on(ov2685);
|
||||||
OV2685_REG_VALUE_16BIT, &id);
|
if (ret)
|
||||||
if (id != CHIP_ID) {
|
|
||||||
dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
|
|
||||||
return ret;
|
return ret;
|
||||||
|
ov2685_read_reg(client, OV2685_REG_CHIP_ID,
|
||||||
|
OV2685_REG_VALUE_16BIT, &id);
|
||||||
|
__ov2685_power_off(ov2685);
|
||||||
|
|
||||||
|
if (id != CHIP_ID) {
|
||||||
|
dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
|
dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
|
||||||
@@ -698,29 +811,38 @@ static int ov2685_check_sensor_id(struct ov2685 *ov2685,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ov2685_configure_regulators(struct ov2685 *ov2685)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < OV2685_NUM_SUPPLIES; i++)
|
|
||||||
ov2685->supplies[i].supply = ov2685_supply_names[i];
|
|
||||||
|
|
||||||
return devm_regulator_bulk_get(&ov2685->client->dev,
|
|
||||||
OV2685_NUM_SUPPLIES,
|
|
||||||
ov2685->supplies);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ov2685_probe(struct i2c_client *client,
|
static int ov2685_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov2685 *ov2685;
|
struct ov2685 *ov2685;
|
||||||
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
|
ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
|
||||||
if (!ov2685)
|
if (!ov2685)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov2685->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov2685->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov2685->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov2685->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov2685->client = client;
|
ov2685->client = client;
|
||||||
ov2685->cur_mode = &supported_modes[0];
|
ov2685->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -729,13 +851,6 @@ static int ov2685_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov2685->xvclk, OV2685_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(ov2685->reset_gpio)) {
|
if (IS_ERR(ov2685->reset_gpio)) {
|
||||||
@@ -743,59 +858,66 @@ static int ov2685_probe(struct i2c_client *client,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ov2685_configure_regulators(ov2685);
|
ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
|
||||||
if (ret) {
|
if (IS_ERR(ov2685->avdd_regulator)) {
|
||||||
dev_err(dev, "Failed to get power regulators\n");
|
dev_err(dev, "Failed to get avdd-supply\n");
|
||||||
return ret;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
|
||||||
|
if (IS_ERR(ov2685->dovdd_regulator)) {
|
||||||
|
dev_err(dev, "Failed to get dovdd-supply\n");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&ov2685->mutex);
|
mutex_init(&ov2685->mutex);
|
||||||
v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
|
v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
|
||||||
ret = ov2685_initialize_controls(ov2685);
|
ret = ov2685_initialize_controls(ov2685);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_destroy_mutex;
|
goto destroy_mutex;
|
||||||
|
|
||||||
ret = __ov2685_power_on(ov2685);
|
|
||||||
if (ret)
|
|
||||||
goto err_free_handler;
|
|
||||||
|
|
||||||
ret = ov2685_check_sensor_id(ov2685, client);
|
ret = ov2685_check_sensor_id(ov2685, client);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_power_off;
|
return ret;
|
||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
ov2685->subdev.internal_ops = &ov2685_internal_ops;
|
ov2685->subdev.internal_ops = &ov2685_internal_ops;
|
||||||
ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
|
ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
|
ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto free_ctrl_handler;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(&ov2685->subdev);
|
sd = &ov2685->subdev;
|
||||||
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov2685->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov2685->module_index, facing,
|
||||||
|
OV2685_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto clean_entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_set_active(dev);
|
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
pm_runtime_idle(dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_clean_entity:
|
clean_entity:
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
media_entity_cleanup(&ov2685->subdev.entity);
|
media_entity_cleanup(&ov2685->subdev.entity);
|
||||||
#endif
|
#endif
|
||||||
err_power_off:
|
free_ctrl_handler:
|
||||||
__ov2685_power_off(ov2685);
|
|
||||||
err_free_handler:
|
|
||||||
v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
|
v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
|
||||||
err_destroy_mutex:
|
destroy_mutex:
|
||||||
mutex_destroy(&ov2685->mutex);
|
mutex_destroy(&ov2685->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -803,37 +925,28 @@ err_destroy_mutex:
|
|||||||
|
|
||||||
static int ov2685_remove(struct i2c_client *client)
|
static int ov2685_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct ov2685 *ov2685 = i2c_get_clientdata(client);
|
||||||
struct ov2685 *ov2685 = to_ov2685(sd);
|
|
||||||
|
|
||||||
v4l2_async_unregister_subdev(sd);
|
__ov2685_power_off(ov2685);
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
v4l2_async_unregister_subdev(&ov2685->subdev);
|
||||||
media_entity_cleanup(&sd->entity);
|
media_entity_cleanup(&ov2685->subdev.entity);
|
||||||
#endif
|
|
||||||
v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
|
v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
|
||||||
mutex_destroy(&ov2685->mutex);
|
mutex_destroy(&ov2685->mutex);
|
||||||
|
|
||||||
pm_runtime_disable(&client->dev);
|
|
||||||
if (!pm_runtime_status_suspended(&client->dev))
|
|
||||||
__ov2685_power_off(ov2685);
|
|
||||||
pm_runtime_set_suspended(&client->dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_OF)
|
|
||||||
static const struct of_device_id ov2685_of_match[] = {
|
static const struct of_device_id ov2685_of_match[] = {
|
||||||
{ .compatible = "ovti,ov2685" },
|
{ .compatible = "ovti,ov2685" },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, ov2685_of_match);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct i2c_driver ov2685_i2c_driver = {
|
static struct i2c_driver ov2685_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov2685",
|
.name = OV2685_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.pm = &ov2685_pm_ops,
|
.pm = &ov2685_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov2685_of_match),
|
.of_match_table = ov2685_of_match
|
||||||
},
|
},
|
||||||
.probe = &ov2685_probe,
|
.probe = &ov2685_probe,
|
||||||
.remove = &ov2685_remove,
|
.remove = &ov2685_remove,
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* ov2718 driver
|
* ov2718 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2018 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,6 +17,9 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
@@ -28,6 +34,8 @@
|
|||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
#include <linux/rk-preisp.h>
|
#include <linux/rk-preisp.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -89,6 +97,8 @@
|
|||||||
#define OF_CAMERA_MODULE_REGULATORS "rockchip,regulator-names"
|
#define OF_CAMERA_MODULE_REGULATORS "rockchip,regulator-names"
|
||||||
#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,regulator-voltages"
|
#define OF_CAMERA_MODULE_REGULATOR_VOLTAGES "rockchip,regulator-voltages"
|
||||||
|
|
||||||
|
#define OV2718_NAME "ov2718"
|
||||||
|
|
||||||
struct ov2718_gpio {
|
struct ov2718_gpio {
|
||||||
int pltfrm_gpio;
|
int pltfrm_gpio;
|
||||||
const char *label;
|
const char *label;
|
||||||
@@ -144,11 +154,16 @@ struct ov2718 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
bool has_devnode;
|
bool has_devnode;
|
||||||
const struct ov2718_mode *cur_mode;
|
const struct ov2718_mode *cur_mode;
|
||||||
const struct ov2718_mode *support_modes;
|
const struct ov2718_mode *support_modes;
|
||||||
u32 support_modes_num;
|
u32 support_modes_num;
|
||||||
struct ov2718_regulators regulators;
|
struct ov2718_regulators regulators;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov2718(sd) container_of(sd, struct ov2718, subdev)
|
#define to_ov2718(sd) container_of(sd, struct ov2718, subdev)
|
||||||
@@ -4275,6 +4290,16 @@ static void ov2718_get_hcg_reg(u32 gain, u32 *again_reg, u32 *dgain_reg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ov2718_get_module_inf(struct ov2718 *ov2718,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV2718_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov2718->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov2718->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
static long ov2718_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
static long ov2718_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
{
|
{
|
||||||
struct ov2718 *ov2718 = to_ov2718(sd);
|
struct ov2718 *ov2718 = to_ov2718(sd);
|
||||||
@@ -4373,13 +4398,90 @@ static long ov2718_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|||||||
l_dgain,
|
l_dgain,
|
||||||
s_dgain);
|
s_dgain);
|
||||||
break;
|
break;
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov2718_get_module_inf(ov2718, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -ENOTTY;
|
return -ENOIOCTLCMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov2718_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov2718_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov2718_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int ov2718_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov2718 *ov2718 = to_ov2718(sd);
|
||||||
|
struct i2c_client *client = ov2718->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov2718->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (ov2718->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov2718->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
ov2718->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&ov2718->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 ov2718_cal_delay(u32 cycles)
|
static inline u32 ov2718_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -4400,13 +4502,16 @@ static int __ov2718_power_on(struct ov2718 *ov2718)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(dev, "could not set pins\n");
|
dev_err(dev, "could not set pins\n");
|
||||||
}
|
}
|
||||||
|
ret = clk_set_rate(ov2718->xvclk, OV2718_XVCLK_FREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
if (clk_get_rate(ov2718->xvclk) != OV2718_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(ov2718->xvclk);
|
ret = clk_prepare_enable(ov2718->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ov2718->regulators.regulator) {
|
if (ov2718->regulators.regulator) {
|
||||||
for (i = 0; i < ov2718->regulators.cnt; i++) {
|
for (i = 0; i < ov2718->regulators.cnt; i++) {
|
||||||
regulator = ov2718->regulators.regulator + i;
|
regulator = ov2718->regulators.regulator + i;
|
||||||
@@ -4554,7 +4659,11 @@ static const struct v4l2_subdev_pad_ops ov2718_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_core_ops ov2718_core_ops = {
|
static const struct v4l2_subdev_core_ops ov2718_core_ops = {
|
||||||
|
.s_power = ov2718_s_power,
|
||||||
.ioctl = ov2718_ioctl,
|
.ioctl = ov2718_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov2718_compat_ioctl32,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov2718_subdev_ops = {
|
static const struct v4l2_subdev_ops ov2718_subdev_ops = {
|
||||||
@@ -4785,6 +4894,10 @@ static int ov2718_check_sensor_id(struct ov2718 *ov2718,
|
|||||||
usleep_range(1000, 2000);
|
usleep_range(1000, 2000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id != CHIP_ID)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -4792,7 +4905,6 @@ static int ov2718_check_sensor_id(struct ov2718 *ov2718,
|
|||||||
|
|
||||||
static int ov2718_analyze_dts(struct ov2718 *ov2718)
|
static int ov2718_analyze_dts(struct ov2718 *ov2718)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
int elem_size, elem_index;
|
int elem_size, elem_index;
|
||||||
const char *str = "";
|
const char *str = "";
|
||||||
struct property *prop;
|
struct property *prop;
|
||||||
@@ -4805,13 +4917,6 @@ static int ov2718_analyze_dts(struct ov2718 *ov2718)
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov2718->xvclk, OV2718_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov2718->xvclk) != OV2718_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov2718->pinctrl = devm_pinctrl_get(dev);
|
ov2718->pinctrl = devm_pinctrl_get(dev);
|
||||||
if (!IS_ERR(ov2718->pinctrl)) {
|
if (!IS_ERR(ov2718->pinctrl)) {
|
||||||
@@ -4918,14 +5023,34 @@ static int ov2718_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov2718 *ov2718;
|
struct ov2718 *ov2718;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov2718 = devm_kzalloc(dev, sizeof(*ov2718), GFP_KERNEL);
|
ov2718 = devm_kzalloc(dev, sizeof(*ov2718), GFP_KERNEL);
|
||||||
if (!ov2718)
|
if (!ov2718)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov2718->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov2718->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov2718->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov2718->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov2718->client = client;
|
ov2718->client = client;
|
||||||
|
|
||||||
ret = ov2718_analyze_dts(ov2718);
|
ret = ov2718_analyze_dts(ov2718);
|
||||||
@@ -4963,18 +5088,28 @@ static int ov2718_probe(struct i2c_client *client,
|
|||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &ov2718_internal_ops;
|
sd->internal_ops = &ov2718_internal_ops;
|
||||||
if (ov2718->has_devnode)
|
if (ov2718->has_devnode)
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov2718->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov2718->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &ov2718->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &ov2718->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ov2718->has_devnode) {
|
if (ov2718->has_devnode) {
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov2718->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov2718->module_index, facing,
|
||||||
|
OV2718_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -5037,7 +5172,7 @@ static const struct i2c_device_id ov2718_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver ov2718_i2c_driver = {
|
static struct i2c_driver ov2718_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov2718",
|
.name = OV2718_NAME,
|
||||||
.pm = &ov2718_pm_ops,
|
.pm = &ov2718_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov2718_of_match),
|
.of_match_table = of_match_ptr(ov2718_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* ov2735 driver
|
* ov2735 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,11 +17,16 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -85,6 +93,8 @@
|
|||||||
#define ANALOG_GAIN_STEP 1
|
#define ANALOG_GAIN_STEP 1
|
||||||
#define ANALOG_GAIN_DEFAULT 0x10
|
#define ANALOG_GAIN_DEFAULT 0x10
|
||||||
|
|
||||||
|
#define OV2735_NAME "ov2735"
|
||||||
|
|
||||||
static const char * const ov2735_supply_names[] = {
|
static const char * const ov2735_supply_names[] = {
|
||||||
"avdd", /* Analog power */
|
"avdd", /* Analog power */
|
||||||
"dovdd", /* Digital I/O power */
|
"dovdd", /* Digital I/O power */
|
||||||
@@ -126,7 +136,12 @@ struct ov2735 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct ov2735_mode *cur_mode;
|
const struct ov2735_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov2735(sd) container_of(sd, struct ov2735, subdev)
|
#define to_ov2735(sd) container_of(sd, struct ov2735, subdev)
|
||||||
@@ -504,14 +519,80 @@ static int ov2735_enable_test_pattern(struct ov2735 *ov2735, u32 pattern)
|
|||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ov2735_get_module_inf(struct ov2735 *ov2735,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV2735_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov2735->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov2735->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ov2735_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct ov2735 *ov2735 = to_ov2735(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov2735_get_module_inf(ov2735, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov2735_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov2735_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov2735_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __ov2735_start_stream(struct ov2735 *ov2735)
|
static int __ov2735_start_stream(struct ov2735 *ov2735)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ov2735_write_array(ov2735->client, ov2735_global_regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
mdelay(3);
|
|
||||||
ret = ov2735_write_array(ov2735->client, ov2735->cur_mode->reg_list);
|
ret = ov2735_write_array(ov2735->client, ov2735->cur_mode->reg_list);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -577,6 +658,44 @@ unlock_and_return:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov2735_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov2735 *ov2735 = to_ov2735(sd);
|
||||||
|
struct i2c_client *client = ov2735->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov2735->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (ov2735->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov2735_write_array(ov2735->client, ov2735_global_regs);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(sd, "could not set init registers\n");
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov2735->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
ov2735->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&ov2735->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 ov2735_cal_delay(u32 cycles)
|
static inline u32 ov2735_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -610,11 +729,14 @@ static int __ov2735_power_on(struct ov2735 *ov2735)
|
|||||||
gpiod_set_value_cansleep(ov2735->reset_gpio, 1);
|
gpiod_set_value_cansleep(ov2735->reset_gpio, 1);
|
||||||
usleep_range(2000, 5000);
|
usleep_range(2000, 5000);
|
||||||
}
|
}
|
||||||
if (!IS_ERR(ov2735->xvclk)) {
|
ret = clk_set_rate(ov2735->xvclk, OV2735_XVCLK_FREQ);
|
||||||
ret = clk_prepare_enable(ov2735->xvclk);
|
if (ret < 0)
|
||||||
if (ret < 0)
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
dev_info(dev, "Failed to enable xvclk\n");
|
if (clk_get_rate(ov2735->xvclk) != OV2735_XVCLK_FREQ)
|
||||||
}
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
|
ret = clk_prepare_enable(ov2735->xvclk);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_info(dev, "Failed to enable xvclk\n");
|
||||||
|
|
||||||
/* 8192 cycles prior to first SCCB transaction */
|
/* 8192 cycles prior to first SCCB transaction */
|
||||||
delay_us = ov2735_cal_delay(8192);
|
delay_us = ov2735_cal_delay(8192);
|
||||||
@@ -691,6 +813,14 @@ static const struct v4l2_subdev_internal_ops ov2735_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops ov2735_core_ops = {
|
||||||
|
.s_power = ov2735_s_power,
|
||||||
|
.ioctl = ov2735_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov2735_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops ov2735_video_ops = {
|
static const struct v4l2_subdev_video_ops ov2735_video_ops = {
|
||||||
.s_stream = ov2735_s_stream,
|
.s_stream = ov2735_s_stream,
|
||||||
};
|
};
|
||||||
@@ -703,6 +833,7 @@ static const struct v4l2_subdev_pad_ops ov2735_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov2735_subdev_ops = {
|
static const struct v4l2_subdev_ops ov2735_subdev_ops = {
|
||||||
|
.core = &ov2735_core_ops,
|
||||||
.video = &ov2735_video_ops,
|
.video = &ov2735_video_ops,
|
||||||
.pad = &ov2735_pad_ops,
|
.pad = &ov2735_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -852,7 +983,7 @@ static int ov2735_check_sensor_id(struct ov2735 *ov2735,
|
|||||||
ret = ov2735_write_reg(ov2735->client, PAGE_SELECT_REG, PAGE_ZERO);
|
ret = ov2735_write_reg(ov2735->client, PAGE_SELECT_REG, PAGE_ZERO);
|
||||||
ret |= ov2735_read_reg(ov2735->client, OV2735_PIDH_ADDR, &pidh);
|
ret |= ov2735_read_reg(ov2735->client, OV2735_PIDH_ADDR, &pidh);
|
||||||
ret |= ov2735_read_reg(ov2735->client, OV2735_PIDL_ADDR, &pidl);
|
ret |= ov2735_read_reg(ov2735->client, OV2735_PIDL_ADDR, &pidl);
|
||||||
if (IS_ERR_VALUE(ret)) {
|
if (ret) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"register read failed, camera module powered off?\n");
|
"register read failed, camera module powered off?\n");
|
||||||
goto err;
|
goto err;
|
||||||
@@ -891,14 +1022,34 @@ static int ov2735_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov2735 *ov2735;
|
struct ov2735 *ov2735;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov2735 = devm_kzalloc(dev, sizeof(*ov2735), GFP_KERNEL);
|
ov2735 = devm_kzalloc(dev, sizeof(*ov2735), GFP_KERNEL);
|
||||||
if (!ov2735)
|
if (!ov2735)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov2735->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov2735->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov2735->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov2735->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov2735->client = client;
|
ov2735->client = client;
|
||||||
ov2735->cur_mode = &supported_modes[0];
|
ov2735->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -907,13 +1058,6 @@ static int ov2735_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov2735->xvclk, OV2735_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov2735->xvclk) != OV2735_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov2735->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
ov2735->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(ov2735->reset_gpio))
|
if (IS_ERR(ov2735->reset_gpio))
|
||||||
@@ -947,17 +1091,27 @@ static int ov2735_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &ov2735_internal_ops;
|
sd->internal_ops = &ov2735_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov2735->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov2735->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &ov2735->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &ov2735->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov2735->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov2735->module_index, facing,
|
||||||
|
OV2735_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1018,7 +1172,7 @@ static const struct i2c_device_id ov2735_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver ov2735_i2c_driver = {
|
static struct i2c_driver ov2735_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov2735",
|
.name = OV2735_NAME,
|
||||||
.pm = &ov2735_pm_ops,
|
.pm = &ov2735_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov2735_of_match),
|
.of_match_table = of_match_ptr(ov2735_of_match),
|
||||||
},
|
},
|
||||||
@@ -1041,4 +1195,5 @@ device_initcall_sync(sensor_mod_init);
|
|||||||
module_exit(sensor_mod_exit);
|
module_exit(sensor_mod_exit);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("OmniVision ov2735 sensor driver");
|
MODULE_DESCRIPTION("OmniVision ov2735 sensor driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
* ov4689 driver
|
* ov4689 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
|
* V0.0X01.0X03 fix gain range.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,12 +18,17 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x03)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -46,10 +55,10 @@
|
|||||||
#define OV4689_GAIN_H_MASK 0x07
|
#define OV4689_GAIN_H_MASK 0x07
|
||||||
#define OV4689_GAIN_H_SHIFT 8
|
#define OV4689_GAIN_H_SHIFT 8
|
||||||
#define OV4689_GAIN_L_MASK 0xff
|
#define OV4689_GAIN_L_MASK 0xff
|
||||||
#define OV4689_GAIN_MIN 0x10
|
#define OV4689_GAIN_MIN 0x80
|
||||||
#define OV4689_GAIN_MAX 0xf8
|
#define OV4689_GAIN_MAX 0x7f8
|
||||||
#define OV4689_GAIN_STEP 1
|
#define OV4689_GAIN_STEP 1
|
||||||
#define OV4689_GAIN_DEFAULT 0x10
|
#define OV4689_GAIN_DEFAULT 0x80
|
||||||
|
|
||||||
#define OV4689_REG_TEST_PATTERN 0x5040
|
#define OV4689_REG_TEST_PATTERN 0x5040
|
||||||
#define OV4689_TEST_PATTERN_ENABLE 0x80
|
#define OV4689_TEST_PATTERN_ENABLE 0x80
|
||||||
@@ -69,6 +78,8 @@
|
|||||||
#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default"
|
#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default"
|
||||||
#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep"
|
#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep"
|
||||||
|
|
||||||
|
#define OV4689_NAME "ov4689"
|
||||||
|
|
||||||
static const char * const ov4689_supply_names[] = {
|
static const char * const ov4689_supply_names[] = {
|
||||||
"avdd", /* Analog power */
|
"avdd", /* Analog power */
|
||||||
"dovdd", /* Digital I/O power */
|
"dovdd", /* Digital I/O power */
|
||||||
@@ -114,7 +125,12 @@ struct ov4689 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct ov4689_mode *cur_mode;
|
const struct ov4689_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
|
#define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
|
||||||
@@ -635,14 +651,80 @@ static int ov4689_g_frame_interval(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ov4689_get_module_inf(struct ov4689 *ov4689,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV4689_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov4689->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov4689->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ov4689_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct ov4689 *ov4689 = to_ov4689(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov4689_get_module_inf(ov4689, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov4689_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov4689_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov4689_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __ov4689_start_stream(struct ov4689 *ov4689)
|
static int __ov4689_start_stream(struct ov4689 *ov4689)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ov4689_write_array(ov4689->client, ov4689_global_regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = ov4689_write_array(ov4689->client, ov4689->cur_mode->reg_list);
|
ret = ov4689_write_array(ov4689->client, ov4689->cur_mode->reg_list);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -701,6 +783,44 @@ unlock_and_return:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov4689_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov4689 *ov4689 = to_ov4689(sd);
|
||||||
|
struct i2c_client *client = ov4689->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov4689->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (ov4689->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov4689_write_array(ov4689->client, ov4689_global_regs);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(sd, "could not set init registers\n");
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov4689->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
ov4689->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&ov4689->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 ov4689_cal_delay(u32 cycles)
|
static inline u32 ov4689_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -719,13 +839,16 @@ static int __ov4689_power_on(struct ov4689 *ov4689)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(dev, "could not set pins\n");
|
dev_err(dev, "could not set pins\n");
|
||||||
}
|
}
|
||||||
|
ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(ov4689->xvclk);
|
ret = clk_prepare_enable(ov4689->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_ERR(ov4689->reset_gpio))
|
if (!IS_ERR(ov4689->reset_gpio))
|
||||||
gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
|
gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
|
||||||
|
|
||||||
@@ -826,6 +949,14 @@ static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops ov4689_core_ops = {
|
||||||
|
.s_power = ov4689_s_power,
|
||||||
|
.ioctl = ov4689_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov4689_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops ov4689_video_ops = {
|
static const struct v4l2_subdev_video_ops ov4689_video_ops = {
|
||||||
.s_stream = ov4689_s_stream,
|
.s_stream = ov4689_s_stream,
|
||||||
.g_frame_interval = ov4689_g_frame_interval,
|
.g_frame_interval = ov4689_g_frame_interval,
|
||||||
@@ -839,6 +970,7 @@ static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov4689_subdev_ops = {
|
static const struct v4l2_subdev_ops ov4689_subdev_ops = {
|
||||||
|
.core = &ov4689_core_ops,
|
||||||
.video = &ov4689_video_ops,
|
.video = &ov4689_video_ops,
|
||||||
.pad = &ov4689_pad_ops,
|
.pad = &ov4689_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -983,7 +1115,7 @@ static int ov4689_check_sensor_id(struct ov4689 *ov4689,
|
|||||||
OV4689_REG_VALUE_16BIT, &id);
|
OV4689_REG_VALUE_16BIT, &id);
|
||||||
if (id != CHIP_ID) {
|
if (id != CHIP_ID) {
|
||||||
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
|
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
|
||||||
return ret;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
||||||
@@ -1007,14 +1139,34 @@ static int ov4689_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov4689 *ov4689;
|
struct ov4689 *ov4689;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
|
ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
|
||||||
if (!ov4689)
|
if (!ov4689)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov4689->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov4689->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov4689->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov4689->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov4689->client = client;
|
ov4689->client = client;
|
||||||
ov4689->cur_mode = &supported_modes[0];
|
ov4689->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -1023,13 +1175,6 @@ static int ov4689_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov4689->xvclk, OV4689_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov4689->xvclk) != OV4689_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
ov4689->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(ov4689->reset_gpio))
|
if (IS_ERR(ov4689->reset_gpio))
|
||||||
@@ -1080,17 +1225,27 @@ static int ov4689_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &ov4689_internal_ops;
|
sd->internal_ops = &ov4689_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &ov4689->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov4689->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov4689->module_index, facing,
|
||||||
|
OV4689_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1151,7 +1306,7 @@ static const struct i2c_device_id ov4689_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver ov4689_i2c_driver = {
|
static struct i2c_driver ov4689_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov4689",
|
.name = OV4689_NAME,
|
||||||
.pm = &ov4689_pm_ops,
|
.pm = &ov4689_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov4689_of_match),
|
.of_match_table = of_match_ptr(ov4689_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
1506
drivers/media/i2c/ov5648.c
Normal file
1506
drivers/media/i2c/ov5648.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
|||||||
* ov5695 driver
|
* ov5695 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,11 +16,16 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -69,6 +76,12 @@
|
|||||||
#define OV5695_LANES 2
|
#define OV5695_LANES 2
|
||||||
#define OV5695_BITS_PER_SAMPLE 10
|
#define OV5695_BITS_PER_SAMPLE 10
|
||||||
|
|
||||||
|
#define I2C_M_WR 0
|
||||||
|
#define I2C_MSG_MAX 300
|
||||||
|
#define I2C_DATA_MAX (I2C_MSG_MAX * 3)
|
||||||
|
|
||||||
|
#define OV5695_NAME "ov5695"
|
||||||
|
|
||||||
static const char * const ov5695_supply_names[] = {
|
static const char * const ov5695_supply_names[] = {
|
||||||
"avdd", /* Analog power */
|
"avdd", /* Analog power */
|
||||||
"dovdd", /* Digital I/O power */
|
"dovdd", /* Digital I/O power */
|
||||||
@@ -96,6 +109,7 @@ struct ov5695 {
|
|||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct clk *xvclk;
|
struct clk *xvclk;
|
||||||
struct gpio_desc *reset_gpio;
|
struct gpio_desc *reset_gpio;
|
||||||
|
struct gpio_desc *pwdn_gpio;
|
||||||
struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES];
|
struct regulator_bulk_data supplies[OV5695_NUM_SUPPLIES];
|
||||||
|
|
||||||
struct v4l2_subdev subdev;
|
struct v4l2_subdev subdev;
|
||||||
@@ -109,7 +123,12 @@ struct ov5695 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct ov5695_mode *cur_mode;
|
const struct ov5695_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
|
#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
|
||||||
@@ -384,175 +403,18 @@ static const struct regval ov5695_1920x1080_regs[] = {
|
|||||||
* mipi_datarate per lane 840Mbps
|
* mipi_datarate per lane 840Mbps
|
||||||
*/
|
*/
|
||||||
static const struct regval ov5695_1296x972_regs[] = {
|
static const struct regval ov5695_1296x972_regs[] = {
|
||||||
{0x0103, 0x01},
|
|
||||||
{0x0100, 0x00},
|
|
||||||
{0x0300, 0x04},
|
|
||||||
{0x0301, 0x00},
|
|
||||||
{0x0302, 0x69},
|
|
||||||
{0x0303, 0x00},
|
|
||||||
{0x0304, 0x00},
|
|
||||||
{0x0305, 0x01},
|
|
||||||
{0x0307, 0x00},
|
|
||||||
{0x030b, 0x00},
|
|
||||||
{0x030c, 0x00},
|
|
||||||
{0x030d, 0x1e},
|
|
||||||
{0x030e, 0x04},
|
|
||||||
{0x030f, 0x03},
|
|
||||||
{0x0312, 0x01},
|
|
||||||
{0x3000, 0x00},
|
|
||||||
{0x3002, 0xa1},
|
|
||||||
{0x3008, 0x00},
|
|
||||||
{0x3010, 0x00},
|
|
||||||
{0x3016, 0x32},
|
|
||||||
{0x3022, 0x51},
|
|
||||||
{0x3106, 0x15},
|
|
||||||
{0x3107, 0x01},
|
|
||||||
{0x3108, 0x05},
|
|
||||||
{0x3500, 0x00},
|
|
||||||
{0x3501, 0x3e},
|
{0x3501, 0x3e},
|
||||||
{0x3502, 0x00},
|
|
||||||
{0x3503, 0x08},
|
|
||||||
{0x3504, 0x03},
|
|
||||||
{0x3505, 0x8c},
|
|
||||||
{0x3507, 0x03},
|
|
||||||
{0x3508, 0x00},
|
|
||||||
{0x3509, 0x10},
|
|
||||||
{0x350c, 0x00},
|
|
||||||
{0x350d, 0x80},
|
|
||||||
{0x3510, 0x00},
|
|
||||||
{0x3511, 0x02},
|
|
||||||
{0x3512, 0x00},
|
|
||||||
{0x3601, 0x55},
|
|
||||||
{0x3602, 0x58},
|
|
||||||
{0x3611, 0x58},
|
{0x3611, 0x58},
|
||||||
{0x3614, 0x30},
|
|
||||||
{0x3615, 0x77},
|
|
||||||
{0x3621, 0x08},
|
|
||||||
{0x3624, 0x40},
|
|
||||||
{0x3633, 0x0c},
|
|
||||||
{0x3634, 0x0c},
|
|
||||||
{0x3635, 0x0c},
|
|
||||||
{0x3636, 0x0c},
|
|
||||||
{0x3638, 0x00},
|
|
||||||
{0x3639, 0x00},
|
|
||||||
{0x363a, 0x00},
|
|
||||||
{0x363b, 0x00},
|
|
||||||
{0x363c, 0xff},
|
|
||||||
{0x363d, 0xfa},
|
|
||||||
{0x3650, 0x44},
|
|
||||||
{0x3651, 0x44},
|
|
||||||
{0x3652, 0x44},
|
|
||||||
{0x3653, 0x44},
|
|
||||||
{0x3654, 0x44},
|
|
||||||
{0x3655, 0x44},
|
|
||||||
{0x3656, 0x44},
|
|
||||||
{0x3657, 0x44},
|
|
||||||
{0x3660, 0x00},
|
|
||||||
{0x3661, 0x00},
|
|
||||||
{0x3662, 0x00},
|
|
||||||
{0x366a, 0x00},
|
|
||||||
{0x366e, 0x0c},
|
|
||||||
{0x3673, 0x04},
|
|
||||||
{0x3700, 0x14},
|
|
||||||
{0x3703, 0x0c},
|
|
||||||
{0x3706, 0x24},
|
{0x3706, 0x24},
|
||||||
{0x3714, 0x27},
|
{0x3714, 0x27},
|
||||||
{0x3715, 0x01},
|
|
||||||
{0x3716, 0x00},
|
{0x3716, 0x00},
|
||||||
{0x3717, 0x02},
|
{0x3717, 0x02},
|
||||||
{0x3733, 0x10},
|
|
||||||
{0x3734, 0x40},
|
|
||||||
{0x373f, 0xa0},
|
|
||||||
{0x3765, 0x20},
|
|
||||||
{0x37a1, 0x1d},
|
|
||||||
{0x37a8, 0x26},
|
|
||||||
{0x37ab, 0x14},
|
|
||||||
{0x37c2, 0x04},
|
|
||||||
{0x37c3, 0xf0},
|
{0x37c3, 0xf0},
|
||||||
{0x37cb, 0x09},
|
|
||||||
{0x37cc, 0x13},
|
|
||||||
{0x37cd, 0x1f},
|
|
||||||
{0x37ce, 0x1f},
|
|
||||||
{0x3800, 0x00},
|
|
||||||
{0x3801, 0x00},
|
|
||||||
{0x3802, 0x00},
|
|
||||||
{0x3803, 0x00},
|
|
||||||
{0x3804, 0x0a},
|
|
||||||
{0x3805, 0x3f},
|
|
||||||
{0x3806, 0x07},
|
|
||||||
{0x3807, 0xaf},
|
|
||||||
{0x3808, 0x05},
|
|
||||||
{0x3809, 0x10},
|
|
||||||
{0x380a, 0x03},
|
|
||||||
{0x380b, 0xcc},
|
|
||||||
{0x380c, 0x02},
|
|
||||||
{0x380d, 0xe4},
|
{0x380d, 0xe4},
|
||||||
{0x380e, 0x03},
|
{0x380e, 0x03},
|
||||||
{0x380f, 0xf4},
|
{0x380f, 0xf4},
|
||||||
{0x3810, 0x00},
|
|
||||||
{0x3811, 0x00},
|
{0x3811, 0x00},
|
||||||
{0x3812, 0x00},
|
|
||||||
{0x3813, 0x06},
|
|
||||||
{0x3814, 0x03},
|
|
||||||
{0x3815, 0x01},
|
|
||||||
{0x3816, 0x03},
|
|
||||||
{0x3817, 0x01},
|
|
||||||
{0x3818, 0x00},
|
|
||||||
{0x3819, 0x00},
|
|
||||||
{0x381a, 0x00},
|
|
||||||
{0x381b, 0x01},
|
|
||||||
{0x3820, 0x8b},
|
|
||||||
{0x3821, 0x01},
|
|
||||||
{0x3c80, 0x08},
|
|
||||||
{0x3c82, 0x00},
|
|
||||||
{0x3c83, 0x00},
|
|
||||||
{0x3c88, 0x00},
|
|
||||||
{0x3d85, 0x14},
|
|
||||||
{0x3f02, 0x08},
|
|
||||||
{0x3f03, 0x10},
|
|
||||||
{0x4008, 0x02},
|
|
||||||
{0x4009, 0x09},
|
|
||||||
{0x404e, 0x20},
|
|
||||||
{0x4501, 0x00},
|
|
||||||
{0x4502, 0x10},
|
|
||||||
{0x4800, 0x00},
|
|
||||||
{0x481f, 0x2a},
|
|
||||||
{0x4837, 0x13},
|
|
||||||
{0x5000, 0x13},
|
{0x5000, 0x13},
|
||||||
{0x5780, 0x3e},
|
|
||||||
{0x5781, 0x0f},
|
|
||||||
{0x5782, 0x44},
|
|
||||||
{0x5783, 0x02},
|
|
||||||
{0x5784, 0x01},
|
|
||||||
{0x5785, 0x01},
|
|
||||||
{0x5786, 0x00},
|
|
||||||
{0x5787, 0x04},
|
|
||||||
{0x5788, 0x02},
|
|
||||||
{0x5789, 0x0f},
|
|
||||||
{0x578a, 0xfd},
|
|
||||||
{0x578b, 0xf5},
|
|
||||||
{0x578c, 0xf5},
|
|
||||||
{0x578d, 0x03},
|
|
||||||
{0x578e, 0x08},
|
|
||||||
{0x578f, 0x0c},
|
|
||||||
{0x5790, 0x08},
|
|
||||||
{0x5791, 0x06},
|
|
||||||
{0x5792, 0x00},
|
|
||||||
{0x5793, 0x52},
|
|
||||||
{0x5794, 0xa3},
|
|
||||||
{0x5b00, 0x00},
|
|
||||||
{0x5b01, 0x1c},
|
|
||||||
{0x5b02, 0x00},
|
|
||||||
{0x5b03, 0x7f},
|
|
||||||
{0x5b05, 0x6c},
|
|
||||||
{0x5e10, 0xfc},
|
|
||||||
{0x4010, 0xf1},
|
|
||||||
{0x3503, 0x08},
|
|
||||||
{0x3505, 0x8c},
|
|
||||||
{0x3507, 0x03},
|
|
||||||
{0x3508, 0x00},
|
|
||||||
{0x3509, 0xf8},
|
|
||||||
{0x0100, 0x01},
|
|
||||||
{REG_NULL, 0x00}
|
{REG_NULL, 0x00}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -733,14 +595,58 @@ static int ov5695_write_reg(struct i2c_client *client, u16 reg,
|
|||||||
static int ov5695_write_array(struct i2c_client *client,
|
static int ov5695_write_array(struct i2c_client *client,
|
||||||
const struct regval *regs)
|
const struct regval *regs)
|
||||||
{
|
{
|
||||||
u32 i;
|
u8 *data;
|
||||||
|
u32 i, j = 0, k = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
|
||||||
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
|
msg = kmalloc((sizeof(struct i2c_msg) * I2C_MSG_MAX),
|
||||||
ret = ov5695_write_reg(client, regs[i].addr,
|
GFP_KERNEL);
|
||||||
OV5695_REG_VALUE_08BIT, regs[i].val);
|
if (!msg)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
return ret;
|
data = kmalloc((sizeof(unsigned char) * I2C_DATA_MAX),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!data) {
|
||||||
|
kfree(msg);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; regs[i].addr != REG_NULL; i++) {
|
||||||
|
(msg + j)->addr = client->addr;
|
||||||
|
(msg + j)->flags = I2C_M_WR;
|
||||||
|
(msg + j)->buf = (data + k);
|
||||||
|
|
||||||
|
data[k + 0] = (u8)(regs[i].addr >> 8);
|
||||||
|
data[k + 1] = (u8)(regs[i].addr & 0xFF);
|
||||||
|
data[k + 2] = (u8)(regs[i].val & 0xFF);
|
||||||
|
k = k + 3;
|
||||||
|
(msg + j)->len = 3;
|
||||||
|
|
||||||
|
if (j++ == (I2C_MSG_MAX - 1)) {
|
||||||
|
ret = i2c_transfer(client->adapter, msg, j);
|
||||||
|
if (ret < 0) {
|
||||||
|
kfree(msg);
|
||||||
|
kfree(data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
k = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j != 0) {
|
||||||
|
ret = i2c_transfer(client->adapter, msg, j);
|
||||||
|
if (ret < 0) {
|
||||||
|
kfree(msg);
|
||||||
|
kfree(data);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kfree(msg);
|
||||||
|
kfree(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read registers up to 4 at a time */
|
/* Read registers up to 4 at a time */
|
||||||
@@ -753,7 +659,7 @@ static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
|
|||||||
__be16 reg_addr_be = cpu_to_be16(reg);
|
__be16 reg_addr_be = cpu_to_be16(reg);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (len > 4)
|
if (len > 4 || !len)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
data_be_p = (u8 *)&data_be;
|
data_be_p = (u8 *)&data_be;
|
||||||
@@ -911,19 +817,102 @@ static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
|
|||||||
OV5695_REG_VALUE_08BIT, val);
|
OV5695_REG_VALUE_08BIT, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov5695_g_frame_interval(struct v4l2_subdev *sd,
|
||||||
|
struct v4l2_subdev_frame_interval *fi)
|
||||||
|
{
|
||||||
|
struct ov5695 *ov5695 = to_ov5695(sd);
|
||||||
|
const struct ov5695_mode *mode = ov5695->cur_mode;
|
||||||
|
|
||||||
|
mutex_lock(&ov5695->mutex);
|
||||||
|
fi->interval.numerator = 10000;
|
||||||
|
fi->interval.denominator = mode->max_fps * 10000;
|
||||||
|
mutex_unlock(&ov5695->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ov5695_get_module_inf(struct ov5695 *ov5695,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV5695_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov5695->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov5695->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ov5695_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct ov5695 *ov5695 = to_ov5695(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov5695_get_module_inf(ov5695, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov5695_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov5695_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov5695_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __ov5695_start_stream(struct ov5695 *ov5695)
|
static int __ov5695_start_stream(struct ov5695 *ov5695)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
|
ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* In case these controls are set before streaming */
|
/* In case these controls are set before streaming */
|
||||||
ret = __v4l2_ctrl_handler_setup(&ov5695->ctrl_handler);
|
mutex_unlock(&ov5695->mutex);
|
||||||
|
ret = v4l2_ctrl_handler_setup(&ov5695->ctrl_handler);
|
||||||
|
mutex_lock(&ov5695->mutex);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@@ -974,6 +963,44 @@ unlock_and_return:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov5695_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov5695 *ov5695 = to_ov5695(sd);
|
||||||
|
struct i2c_client *client = ov5695->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov5695->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (ov5695->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(sd, "could not set init registers\n");
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov5695->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
ov5695->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&ov5695->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 ov5695_cal_delay(u32 cycles)
|
static inline u32 ov5695_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -986,13 +1013,21 @@ static int __ov5695_power_on(struct ov5695 *ov5695)
|
|||||||
u32 delay_us;
|
u32 delay_us;
|
||||||
struct device *dev = &ov5695->client->dev;
|
struct device *dev = &ov5695->client->dev;
|
||||||
|
|
||||||
|
ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(ov5695->xvclk);
|
ret = clk_prepare_enable(ov5695->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
|
if (!IS_ERR(ov5695->reset_gpio))
|
||||||
|
gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
|
||||||
|
|
||||||
ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies);
|
ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -1000,7 +1035,11 @@ static int __ov5695_power_on(struct ov5695 *ov5695)
|
|||||||
goto disable_clk;
|
goto disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
|
if (!IS_ERR(ov5695->reset_gpio))
|
||||||
|
gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
|
||||||
|
|
||||||
|
if (!IS_ERR(ov5695->pwdn_gpio))
|
||||||
|
gpiod_set_value_cansleep(ov5695->pwdn_gpio, 1);
|
||||||
|
|
||||||
/* 8192 cycles prior to first SCCB transaction */
|
/* 8192 cycles prior to first SCCB transaction */
|
||||||
delay_us = ov5695_cal_delay(8192);
|
delay_us = ov5695_cal_delay(8192);
|
||||||
@@ -1016,12 +1055,15 @@ disable_clk:
|
|||||||
|
|
||||||
static void __ov5695_power_off(struct ov5695 *ov5695)
|
static void __ov5695_power_off(struct ov5695 *ov5695)
|
||||||
{
|
{
|
||||||
|
if (!IS_ERR(ov5695->pwdn_gpio))
|
||||||
|
gpiod_set_value_cansleep(ov5695->pwdn_gpio, 0);
|
||||||
clk_disable_unprepare(ov5695->xvclk);
|
clk_disable_unprepare(ov5695->xvclk);
|
||||||
gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
|
if (!IS_ERR(ov5695->reset_gpio))
|
||||||
|
gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
|
||||||
regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies);
|
regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused ov5695_runtime_resume(struct device *dev)
|
static int ov5695_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
@@ -1030,7 +1072,7 @@ static int __maybe_unused ov5695_runtime_resume(struct device *dev)
|
|||||||
return __ov5695_power_on(ov5695);
|
return __ov5695_power_on(ov5695);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused ov5695_runtime_suspend(struct device *dev)
|
static int ov5695_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
||||||
@@ -1074,8 +1116,17 @@ static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops ov5695_core_ops = {
|
||||||
|
.s_power = ov5695_s_power,
|
||||||
|
.ioctl = ov5695_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov5695_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops ov5695_video_ops = {
|
static const struct v4l2_subdev_video_ops ov5695_video_ops = {
|
||||||
.s_stream = ov5695_s_stream,
|
.s_stream = ov5695_s_stream,
|
||||||
|
.g_frame_interval = ov5695_g_frame_interval,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_pad_ops ov5695_pad_ops = {
|
static const struct v4l2_subdev_pad_ops ov5695_pad_ops = {
|
||||||
@@ -1086,6 +1137,7 @@ static const struct v4l2_subdev_pad_ops ov5695_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov5695_subdev_ops = {
|
static const struct v4l2_subdev_ops ov5695_subdev_ops = {
|
||||||
|
.core = &ov5695_core_ops,
|
||||||
.video = &ov5695_video_ops,
|
.video = &ov5695_video_ops,
|
||||||
.pad = &ov5695_pad_ops,
|
.pad = &ov5695_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -1110,7 +1162,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pm_runtime_get_if_in_use(&client->dev) <= 0)
|
if (pm_runtime_get(&client->dev) <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (ctrl->id) {
|
switch (ctrl->id) {
|
||||||
@@ -1127,7 +1179,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
|
ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
|
||||||
OV5695_REG_VALUE_08BIT,
|
OV5695_REG_VALUE_08BIT,
|
||||||
ctrl->val & OV5695_DIGI_GAIN_L_MASK);
|
ctrl->val & OV5695_DIGI_GAIN_L_MASK);
|
||||||
ret = ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
|
ret |= ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
|
||||||
OV5695_REG_VALUE_08BIT,
|
OV5695_REG_VALUE_08BIT,
|
||||||
ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT);
|
ctrl->val >> OV5695_DIGI_GAIN_H_SHIFT);
|
||||||
break;
|
break;
|
||||||
@@ -1143,7 +1195,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
|
|||||||
dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
|
||||||
__func__, ctrl->id, ctrl->val);
|
__func__, ctrl->id, ctrl->val);
|
||||||
break;
|
break;
|
||||||
};
|
}
|
||||||
|
|
||||||
pm_runtime_put(&client->dev);
|
pm_runtime_put(&client->dev);
|
||||||
|
|
||||||
@@ -1240,7 +1292,7 @@ static int ov5695_check_sensor_id(struct ov5695 *ov5695,
|
|||||||
OV5695_REG_VALUE_24BIT, &id);
|
OV5695_REG_VALUE_24BIT, &id);
|
||||||
if (id != CHIP_ID) {
|
if (id != CHIP_ID) {
|
||||||
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
|
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
|
||||||
return ret;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
|
||||||
@@ -1264,14 +1316,34 @@ static int ov5695_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov5695 *ov5695;
|
struct ov5695 *ov5695;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
|
ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
|
||||||
if (!ov5695)
|
if (!ov5695)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov5695->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov5695->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov5695->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov5695->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov5695->client = client;
|
ov5695->client = client;
|
||||||
ov5695->cur_mode = &supported_modes[0];
|
ov5695->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -1280,20 +1352,16 @@ static int ov5695_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov5695->xvclk, OV5695_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(ov5695->reset_gpio)) {
|
if (IS_ERR(ov5695->reset_gpio)) {
|
||||||
dev_err(dev, "Failed to get reset-gpios\n");
|
dev_warn(dev, "Failed to get reset-gpios\n");
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ov5695->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
|
||||||
|
if (IS_ERR(ov5695->pwdn_gpio))
|
||||||
|
dev_warn(dev, "Failed to get pwdn-gpios\n");
|
||||||
|
|
||||||
ret = ov5695_configure_regulators(ov5695);
|
ret = ov5695_configure_regulators(ov5695);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Failed to get power regulators\n");
|
dev_err(dev, "Failed to get power regulators\n");
|
||||||
@@ -1318,7 +1386,8 @@ static int ov5695_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &ov5695_internal_ops;
|
sd->internal_ops = &ov5695_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
@@ -1328,7 +1397,16 @@ static int ov5695_probe(struct i2c_client *client,
|
|||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov5695->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov5695->module_index, facing,
|
||||||
|
OV5695_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1382,17 +1460,34 @@ static const struct of_device_id ov5695_of_match[] = {
|
|||||||
MODULE_DEVICE_TABLE(of, ov5695_of_match);
|
MODULE_DEVICE_TABLE(of, ov5695_of_match);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct i2c_device_id ov5695_match_id[] = {
|
||||||
|
{ "ovti,ov5695", 0 },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
static struct i2c_driver ov5695_i2c_driver = {
|
static struct i2c_driver ov5695_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov5695",
|
.name = OV5695_NAME,
|
||||||
.pm = &ov5695_pm_ops,
|
.pm = &ov5695_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov5695_of_match),
|
.of_match_table = of_match_ptr(ov5695_of_match),
|
||||||
},
|
},
|
||||||
.probe = &ov5695_probe,
|
.probe = &ov5695_probe,
|
||||||
.remove = &ov5695_remove,
|
.remove = &ov5695_remove,
|
||||||
|
.id_table = ov5695_match_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_i2c_driver(ov5695_i2c_driver);
|
static int __init sensor_mod_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&ov5695_i2c_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit sensor_mod_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&ov5695_i2c_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_initcall_sync(sensor_mod_init);
|
||||||
|
module_exit(sensor_mod_exit);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
|
MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,9 @@
|
|||||||
* ov7750 driver
|
* ov7750 driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,12 +17,17 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -72,6 +80,8 @@
|
|||||||
#define OF_CAMERA_PINCTRL_DEFAULT "rockchip,camera_default"
|
#define OF_CAMERA_PINCTRL_DEFAULT "rockchip,camera_default"
|
||||||
#define OF_CAMERA_PINCTRL_SLEEP "rockchip,camera_sleep"
|
#define OF_CAMERA_PINCTRL_SLEEP "rockchip,camera_sleep"
|
||||||
|
|
||||||
|
#define OV7750_NAME "ov7750"
|
||||||
|
|
||||||
static const struct regval *ov7750_global_regs;
|
static const struct regval *ov7750_global_regs;
|
||||||
|
|
||||||
static const char * const ov7750_supply_names[] = {
|
static const char * const ov7750_supply_names[] = {
|
||||||
@@ -119,7 +129,12 @@ struct ov7750 {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct ov7750_mode *cur_mode;
|
const struct ov7750_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_ov7750(sd) container_of(sd, struct ov7750, subdev)
|
#define to_ov7750(sd) container_of(sd, struct ov7750, subdev)
|
||||||
@@ -597,14 +612,80 @@ static int ov7750_enable_test_pattern(struct ov7750 *ov7750, u32 pattern)
|
|||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ov7750_get_module_inf(struct ov7750 *ov7750,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, OV7750_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, ov7750->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, ov7750->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ov7750_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct ov7750 *ov7750 = to_ov7750(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
ov7750_get_module_inf(ov7750, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOTTY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long ov7750_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov7750_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = ov7750_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __ov7750_start_stream(struct ov7750 *ov7750)
|
static int __ov7750_start_stream(struct ov7750 *ov7750)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ov7750_write_array(ov7750->client, ov7750_global_regs);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = ov7750_write_array(ov7750->client, ov7750->cur_mode->reg_list);
|
ret = ov7750_write_array(ov7750->client, ov7750->cur_mode->reg_list);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -667,6 +748,44 @@ unlock_and_return:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ov7750_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct ov7750 *ov7750 = to_ov7750(sd);
|
||||||
|
struct i2c_client *client = ov7750->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&ov7750->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (ov7750->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ov7750_write_array(ov7750->client, ov7750_global_regs);
|
||||||
|
if (ret) {
|
||||||
|
v4l2_err(sd, "could not set init registers\n");
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov7750->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
ov7750->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&ov7750->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 ov7750_cal_delay(u32 cycles)
|
static inline u32 ov7750_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -686,6 +805,11 @@ static int __ov7750_power_on(struct ov7750 *ov7750)
|
|||||||
dev_err(dev, "could not set pins\n");
|
dev_err(dev, "could not set pins\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = clk_set_rate(ov7750->xvclk, OV7750_XVCLK_FREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
if (clk_get_rate(ov7750->xvclk) != OV7750_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(ov7750->xvclk);
|
ret = clk_prepare_enable(ov7750->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
@@ -792,6 +916,14 @@ static const struct v4l2_subdev_internal_ops ov7750_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops ov7750_core_ops = {
|
||||||
|
.s_power = ov7750_s_power,
|
||||||
|
.ioctl = ov7750_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = ov7750_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops ov7750_video_ops = {
|
static const struct v4l2_subdev_video_ops ov7750_video_ops = {
|
||||||
.s_stream = ov7750_s_stream,
|
.s_stream = ov7750_s_stream,
|
||||||
};
|
};
|
||||||
@@ -804,6 +936,7 @@ static const struct v4l2_subdev_pad_ops ov7750_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops ov7750_subdev_ops = {
|
static const struct v4l2_subdev_ops ov7750_subdev_ops = {
|
||||||
|
.core = &ov7750_core_ops,
|
||||||
.video = &ov7750_video_ops,
|
.video = &ov7750_video_ops,
|
||||||
.pad = &ov7750_pad_ops,
|
.pad = &ov7750_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -955,7 +1088,7 @@ static int ov7750_check_sensor_id(struct ov7750 *ov7750,
|
|||||||
OV7750_REG_VALUE_16BIT, &id);
|
OV7750_REG_VALUE_16BIT, &id);
|
||||||
if (id != CHIP_ID) {
|
if (id != CHIP_ID) {
|
||||||
dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
|
dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
|
||||||
return ret;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ov7750_read_reg(client, OV7750_CHIP_REVISION_REG,
|
ret = ov7750_read_reg(client, OV7750_CHIP_REVISION_REG,
|
||||||
@@ -990,14 +1123,34 @@ static int ov7750_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct ov7750 *ov7750;
|
struct ov7750 *ov7750;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
ov7750 = devm_kzalloc(dev, sizeof(*ov7750), GFP_KERNEL);
|
ov7750 = devm_kzalloc(dev, sizeof(*ov7750), GFP_KERNEL);
|
||||||
if (!ov7750)
|
if (!ov7750)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&ov7750->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&ov7750->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&ov7750->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&ov7750->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ov7750->client = client;
|
ov7750->client = client;
|
||||||
ov7750->cur_mode = &supported_modes[0];
|
ov7750->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -1006,13 +1159,6 @@ static int ov7750_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(ov7750->xvclk, OV7750_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(ov7750->xvclk) != OV7750_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
ov7750->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
ov7750->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(ov7750->reset_gpio))
|
if (IS_ERR(ov7750->reset_gpio))
|
||||||
@@ -1061,17 +1207,27 @@ static int ov7750_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &ov7750_internal_ops;
|
sd->internal_ops = &ov7750_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
ov7750->pad.flags = MEDIA_PAD_FL_SOURCE;
|
ov7750->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &ov7750->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &ov7750->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(ov7750->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
ov7750->module_index, facing,
|
||||||
|
OV7750_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1132,7 +1288,7 @@ static const struct i2c_device_id ov7750_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver ov7750_i2c_driver = {
|
static struct i2c_driver ov7750_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ov7750",
|
.name = OV7750_NAME,
|
||||||
.pm = &ov7750_pm_ops,
|
.pm = &ov7750_pm_ops,
|
||||||
.of_match_table = of_match_ptr(ov7750_of_match),
|
.of_match_table = of_match_ptr(ov7750_of_match),
|
||||||
},
|
},
|
||||||
@@ -1155,4 +1311,5 @@ device_initcall_sync(sensor_mod_init);
|
|||||||
module_exit(sensor_mod_exit);
|
module_exit(sensor_mod_exit);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("OmniVision ov7750 sensor driver");
|
MODULE_DESCRIPTION("OmniVision ov7750 sensor driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
||||||
|
|||||||
2999
drivers/media/i2c/ov8858.c
Normal file
2999
drivers/media/i2c/ov8858.c
Normal file
File diff suppressed because it is too large
Load Diff
1172
drivers/media/i2c/ov9281.c
Normal file
1172
drivers/media/i2c/ov9281.c
Normal file
File diff suppressed because it is too large
Load Diff
1293
drivers/media/i2c/ov9750.c
Normal file
1293
drivers/media/i2c/ov9750.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,9 @@
|
|||||||
* sc031gs driver
|
* sc031gs driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
|
||||||
|
*
|
||||||
|
* V0.0X01.0X01 add poweron function.
|
||||||
|
* V0.0X01.0X02 fix mclk issue when probe multiple camera.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
@@ -14,11 +17,16 @@
|
|||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/rk-camera-module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
#include <media/media-entity.h>
|
#include <media/media-entity.h>
|
||||||
#include <media/v4l2-async.h>
|
#include <media/v4l2-async.h>
|
||||||
#include <media/v4l2-ctrls.h>
|
#include <media/v4l2-ctrls.h>
|
||||||
#include <media/v4l2-subdev.h>
|
#include <media/v4l2-subdev.h>
|
||||||
|
|
||||||
|
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02)
|
||||||
|
|
||||||
#ifndef V4L2_CID_DIGITAL_GAIN
|
#ifndef V4L2_CID_DIGITAL_GAIN
|
||||||
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
|
||||||
#endif
|
#endif
|
||||||
@@ -66,6 +74,8 @@
|
|||||||
#define SC031GS_BITS_PER_SAMPLE 10
|
#define SC031GS_BITS_PER_SAMPLE 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SC031GS_NAME "sc031gs"
|
||||||
|
|
||||||
static const char * const sc031gs_supply_names[] = {
|
static const char * const sc031gs_supply_names[] = {
|
||||||
"avdd", /* Analog power */
|
"avdd", /* Analog power */
|
||||||
"dovdd", /* Digital I/O power */
|
"dovdd", /* Digital I/O power */
|
||||||
@@ -106,7 +116,12 @@ struct sc031gs {
|
|||||||
struct v4l2_ctrl *test_pattern;
|
struct v4l2_ctrl *test_pattern;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
bool streaming;
|
bool streaming;
|
||||||
|
bool power_on;
|
||||||
const struct sc031gs_mode *cur_mode;
|
const struct sc031gs_mode *cur_mode;
|
||||||
|
u32 module_index;
|
||||||
|
const char *module_facing;
|
||||||
|
const char *module_name;
|
||||||
|
const char *len_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_sc031gs(sd) container_of(sd, struct sc031gs, subdev)
|
#define to_sc031gs(sd) container_of(sd, struct sc031gs, subdev)
|
||||||
@@ -128,6 +143,7 @@ static const struct regval sc031gs_global_regs[] = {
|
|||||||
{0x3018, 0x1f},
|
{0x3018, 0x1f},
|
||||||
{0x3019, 0xff},
|
{0x3019, 0xff},
|
||||||
{0x301c, 0xb4},
|
{0x301c, 0xb4},
|
||||||
|
{0x3028, 0x82},
|
||||||
{0x320c, 0x03},
|
{0x320c, 0x03},
|
||||||
{0x320d, 0x6e},
|
{0x320d, 0x6e},
|
||||||
// {0x320e, 0x02}, //120fps
|
// {0x320e, 0x02}, //120fps
|
||||||
@@ -197,9 +213,10 @@ static const struct regval sc031gs_global_regs[] = {
|
|||||||
{0x4500, 0x59},
|
{0x4500, 0x59},
|
||||||
{0x4501, 0xc4},
|
{0x4501, 0xc4},
|
||||||
{0x5011, 0x00},
|
{0x5011, 0x00},
|
||||||
// {0x0100, 0x01},
|
{0x0100, 0x01},
|
||||||
{0x4418, 0x08},
|
{0x4418, 0x08},
|
||||||
{0x4419, 0x8a},
|
{0x4419, 0x8e},
|
||||||
|
{0x0100, 0x00},
|
||||||
// test pattern
|
// test pattern
|
||||||
// {0x4501, 0xac},
|
// {0x4501, 0xac},
|
||||||
// {0x5011, 0x01},
|
// {0x5011, 0x01},
|
||||||
@@ -282,8 +299,10 @@ static const struct regval sc031gs_global_regs[] = {
|
|||||||
{0x4501, 0xc4},
|
{0x4501, 0xc4},
|
||||||
{0x4603, 0x00},
|
{0x4603, 0x00},
|
||||||
{0x5011, 0x00},
|
{0x5011, 0x00},
|
||||||
|
{0x0100, 0x01},
|
||||||
{0x4418, 0x08},
|
{0x4418, 0x08},
|
||||||
{0x4419, 0x8a},
|
{0x4419, 0x8e},
|
||||||
|
{0x0100, 0x00},
|
||||||
{REG_NULL, 0x00},
|
{REG_NULL, 0x00},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -353,6 +372,8 @@ static int sc031gs_write_array(struct i2c_client *client,
|
|||||||
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) {
|
for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) {
|
||||||
ret = sc031gs_write_reg(client, regs[i].addr,
|
ret = sc031gs_write_reg(client, regs[i].addr,
|
||||||
SC031GS_REG_VALUE_08BIT, regs[i].val);
|
SC031GS_REG_VALUE_08BIT, regs[i].val);
|
||||||
|
if (regs[i].addr == 0x0100 && regs[i].val == 0x01)
|
||||||
|
msleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -526,6 +547,76 @@ static int sc031gs_enable_test_pattern(struct sc031gs *sc031gs, u32 pattern)
|
|||||||
SC031GS_REG_VALUE_08BIT, val);
|
SC031GS_REG_VALUE_08BIT, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sc031gs_get_module_inf(struct sc031gs *sc031gs,
|
||||||
|
struct rkmodule_inf *inf)
|
||||||
|
{
|
||||||
|
memset(inf, 0, sizeof(*inf));
|
||||||
|
strlcpy(inf->base.sensor, SC031GS_NAME, sizeof(inf->base.sensor));
|
||||||
|
strlcpy(inf->base.module, sc031gs->module_name,
|
||||||
|
sizeof(inf->base.module));
|
||||||
|
strlcpy(inf->base.lens, sc031gs->len_name, sizeof(inf->base.lens));
|
||||||
|
}
|
||||||
|
|
||||||
|
static long sc031gs_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||||
|
{
|
||||||
|
struct sc031gs *sc031gs = to_sc031gs(sd);
|
||||||
|
long ret = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
sc031gs_get_module_inf(sc031gs, (struct rkmodule_inf *)arg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static long sc031gs_compat_ioctl32(struct v4l2_subdev *sd,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
void __user *up = compat_ptr(arg);
|
||||||
|
struct rkmodule_inf *inf;
|
||||||
|
struct rkmodule_awb_cfg *cfg;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case RKMODULE_GET_MODULE_INFO:
|
||||||
|
inf = kzalloc(sizeof(*inf), GFP_KERNEL);
|
||||||
|
if (!inf) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sc031gs_ioctl(sd, cmd, inf);
|
||||||
|
if (!ret)
|
||||||
|
ret = copy_to_user(up, inf, sizeof(*inf));
|
||||||
|
kfree(inf);
|
||||||
|
break;
|
||||||
|
case RKMODULE_AWB_CFG:
|
||||||
|
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||||
|
if (!cfg) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = copy_from_user(cfg, up, sizeof(*cfg));
|
||||||
|
if (!ret)
|
||||||
|
ret = sc031gs_ioctl(sd, cmd, cfg);
|
||||||
|
kfree(cfg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENOIOCTLCMD;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int sc031gs_set_ctrl_gain(struct sc031gs *sc031gs, u32 a_gain)
|
static int sc031gs_set_ctrl_gain(struct sc031gs *sc031gs, u32 a_gain)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -559,12 +650,12 @@ static int sc031gs_set_ctrl_gain(struct sc031gs *sc031gs, u32 a_gain)
|
|||||||
|
|
||||||
if (a_gain < 0x20) {
|
if (a_gain < 0x20) {
|
||||||
ret |= sc031gs_write_reg(sc031gs->client, 0x3314,
|
ret |= sc031gs_write_reg(sc031gs->client, 0x3314,
|
||||||
SC031GS_REG_VALUE_08BIT, 0x3a);
|
SC031GS_REG_VALUE_08BIT, 0x42);
|
||||||
ret |= sc031gs_write_reg(sc031gs->client, 0x3317,
|
ret |= sc031gs_write_reg(sc031gs->client, 0x3317,
|
||||||
SC031GS_REG_VALUE_08BIT, 0x20);
|
SC031GS_REG_VALUE_08BIT, 0x20);
|
||||||
} else {
|
} else {
|
||||||
ret |= sc031gs_write_reg(sc031gs->client, 0x3314,
|
ret |= sc031gs_write_reg(sc031gs->client, 0x3314,
|
||||||
SC031GS_REG_VALUE_08BIT, 0x44);
|
SC031GS_REG_VALUE_08BIT, 0x4f);
|
||||||
ret |= sc031gs_write_reg(sc031gs->client, 0x3317,
|
ret |= sc031gs_write_reg(sc031gs->client, 0x3317,
|
||||||
SC031GS_REG_VALUE_08BIT, 0x0f);
|
SC031GS_REG_VALUE_08BIT, 0x0f);
|
||||||
}
|
}
|
||||||
@@ -659,6 +750,37 @@ static int sc031gs_g_frame_interval(struct v4l2_subdev *sd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sc031gs_s_power(struct v4l2_subdev *sd, int on)
|
||||||
|
{
|
||||||
|
struct sc031gs *sc031gs = to_sc031gs(sd);
|
||||||
|
struct i2c_client *client = sc031gs->client;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(&sc031gs->mutex);
|
||||||
|
|
||||||
|
/* If the power state is not modified - no work to do. */
|
||||||
|
if (sc031gs->power_on == !!on)
|
||||||
|
goto unlock_and_return;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
ret = pm_runtime_get_sync(&client->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
pm_runtime_put_noidle(&client->dev);
|
||||||
|
goto unlock_and_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc031gs->power_on = true;
|
||||||
|
} else {
|
||||||
|
pm_runtime_put(&client->dev);
|
||||||
|
sc031gs->power_on = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_and_return:
|
||||||
|
mutex_unlock(&sc031gs->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Calculate the delay in us by clock rate and clock cycles */
|
/* Calculate the delay in us by clock rate and clock cycles */
|
||||||
static inline u32 sc031gs_cal_delay(u32 cycles)
|
static inline u32 sc031gs_cal_delay(u32 cycles)
|
||||||
{
|
{
|
||||||
@@ -671,6 +793,11 @@ static int __sc031gs_power_on(struct sc031gs *sc031gs)
|
|||||||
u32 delay_us;
|
u32 delay_us;
|
||||||
struct device *dev = &sc031gs->client->dev;
|
struct device *dev = &sc031gs->client->dev;
|
||||||
|
|
||||||
|
ret = clk_set_rate(sc031gs->xvclk, SC031GS_XVCLK_FREQ);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
|
||||||
|
if (clk_get_rate(sc031gs->xvclk) != SC031GS_XVCLK_FREQ)
|
||||||
|
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
||||||
ret = clk_prepare_enable(sc031gs->xvclk);
|
ret = clk_prepare_enable(sc031gs->xvclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "Failed to enable xvclk\n");
|
dev_err(dev, "Failed to enable xvclk\n");
|
||||||
@@ -772,6 +899,14 @@ static const struct v4l2_subdev_internal_ops sc031gs_internal_ops = {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct v4l2_subdev_core_ops sc031gs_core_ops = {
|
||||||
|
.s_power = sc031gs_s_power,
|
||||||
|
.ioctl = sc031gs_ioctl,
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
.compat_ioctl32 = sc031gs_compat_ioctl32,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_video_ops sc031gs_video_ops = {
|
static const struct v4l2_subdev_video_ops sc031gs_video_ops = {
|
||||||
.s_stream = sc031gs_s_stream,
|
.s_stream = sc031gs_s_stream,
|
||||||
.g_frame_interval = sc031gs_g_frame_interval,
|
.g_frame_interval = sc031gs_g_frame_interval,
|
||||||
@@ -788,6 +923,7 @@ static const struct v4l2_subdev_pad_ops sc031gs_pad_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct v4l2_subdev_ops sc031gs_subdev_ops = {
|
static const struct v4l2_subdev_ops sc031gs_subdev_ops = {
|
||||||
|
.core = &sc031gs_core_ops,
|
||||||
.video = &sc031gs_video_ops,
|
.video = &sc031gs_video_ops,
|
||||||
.pad = &sc031gs_pad_ops,
|
.pad = &sc031gs_pad_ops,
|
||||||
};
|
};
|
||||||
@@ -927,7 +1063,7 @@ static int sc031gs_check_sensor_id(struct sc031gs *sc031gs,
|
|||||||
SC031GS_REG_VALUE_16BIT, &id);
|
SC031GS_REG_VALUE_16BIT, &id);
|
||||||
if (id != CHIP_ID) {
|
if (id != CHIP_ID) {
|
||||||
dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
|
dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret);
|
||||||
return ret;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "Detected SC031GS CHIP ID = 0x%04x sensor\n", CHIP_ID);
|
dev_info(dev, "Detected SC031GS CHIP ID = 0x%04x sensor\n", CHIP_ID);
|
||||||
@@ -951,14 +1087,34 @@ static int sc031gs_probe(struct i2c_client *client,
|
|||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
|
struct device_node *node = dev->of_node;
|
||||||
struct sc031gs *sc031gs;
|
struct sc031gs *sc031gs;
|
||||||
struct v4l2_subdev *sd;
|
struct v4l2_subdev *sd;
|
||||||
|
char facing[2];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dev_info(dev, "driver version: %02x.%02x.%02x",
|
||||||
|
DRIVER_VERSION >> 16,
|
||||||
|
(DRIVER_VERSION & 0xff00) >> 8,
|
||||||
|
DRIVER_VERSION & 0x00ff);
|
||||||
|
|
||||||
sc031gs = devm_kzalloc(dev, sizeof(*sc031gs), GFP_KERNEL);
|
sc031gs = devm_kzalloc(dev, sizeof(*sc031gs), GFP_KERNEL);
|
||||||
if (!sc031gs)
|
if (!sc031gs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
|
||||||
|
&sc031gs->module_index);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
|
||||||
|
&sc031gs->module_facing);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
|
||||||
|
&sc031gs->module_name);
|
||||||
|
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
|
||||||
|
&sc031gs->len_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not get module information!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
sc031gs->client = client;
|
sc031gs->client = client;
|
||||||
sc031gs->cur_mode = &supported_modes[0];
|
sc031gs->cur_mode = &supported_modes[0];
|
||||||
|
|
||||||
@@ -967,13 +1123,6 @@ static int sc031gs_probe(struct i2c_client *client,
|
|||||||
dev_err(dev, "Failed to get xvclk\n");
|
dev_err(dev, "Failed to get xvclk\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
ret = clk_set_rate(sc031gs->xvclk, SC031GS_XVCLK_FREQ);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to set xvclk rate (24MHz)\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (clk_get_rate(sc031gs->xvclk) != SC031GS_XVCLK_FREQ)
|
|
||||||
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
|
|
||||||
|
|
||||||
sc031gs->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
sc031gs->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
if (IS_ERR(sc031gs->reset_gpio))
|
if (IS_ERR(sc031gs->reset_gpio))
|
||||||
@@ -1006,17 +1155,27 @@ static int sc031gs_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
|
||||||
sd->internal_ops = &sc031gs_internal_ops;
|
sd->internal_ops = &sc031gs_internal_ops;
|
||||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||||
|
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_MEDIA_CONTROLLER)
|
#if defined(CONFIG_MEDIA_CONTROLLER)
|
||||||
sc031gs->pad.flags = MEDIA_PAD_FL_SOURCE;
|
sc031gs->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||||
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
|
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||||
ret = media_entity_init(&sd->entity, 1, &sc031gs->pad, 0);
|
ret = media_entity_pads_init(&sd->entity, 1, &sc031gs->pad);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_power_off;
|
goto err_power_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = v4l2_async_register_subdev(sd);
|
memset(facing, 0, sizeof(facing));
|
||||||
|
if (strcmp(sc031gs->module_facing, "back") == 0)
|
||||||
|
facing[0] = 'b';
|
||||||
|
else
|
||||||
|
facing[0] = 'f';
|
||||||
|
|
||||||
|
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
|
||||||
|
sc031gs->module_index, facing,
|
||||||
|
SC031GS_NAME, dev_name(sd->dev));
|
||||||
|
ret = v4l2_async_register_subdev_sensor_common(sd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "v4l2 async register subdev failed\n");
|
dev_err(dev, "v4l2 async register subdev failed\n");
|
||||||
goto err_clean_entity;
|
goto err_clean_entity;
|
||||||
@@ -1077,7 +1236,7 @@ static const struct i2c_device_id sc031gs_match_id[] = {
|
|||||||
|
|
||||||
static struct i2c_driver sc031gs_i2c_driver = {
|
static struct i2c_driver sc031gs_i2c_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sc031gs",
|
.name = SC031GS_NAME,
|
||||||
.pm = &sc031gs_pm_ops,
|
.pm = &sc031gs_pm_ops,
|
||||||
.of_match_table = of_match_ptr(sc031gs_of_match),
|
.of_match_table = of_match_ptr(sc031gs_of_match),
|
||||||
},
|
},
|
||||||
|
|||||||
1191
drivers/media/i2c/sc132gs.c
Normal file
1191
drivers/media/i2c/sc132gs.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user