History

Merge tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull media updates from Mauro Carvalho Chehab: - New V4L2 ioctl VIDIOC_REMOVE_BUFS - experimental support for using generic metaformats on V4L2 core - New drivers: Intel IPU6 controller driver, Broadcom BCM283x/BCM271x - More cleanups at atomisp driver - Usual bunch of driver cleanups, improvements and fixes * tag 'media/v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (328 commits) media: bcm2835-unicam: Depend on COMMON_CLK Revert "media: v4l2-ctrls: show all owned controls in log_status" media: ov2740: Ensure proper reset sequence on probe() media: intel/ipu6: Don't print user-triggerable errors to kernel log media: bcm2835-unicam: Fix driver path in MAINTAINERS media: bcm2835-unicam: Fix a NULL vs IS_ERR() check media: bcm2835-unicam: Do not print error when irq not found media: bcm2835-unicam: Do not replace IRQ retcode during probe media: bcm2835-unicam: Convert to platform remove callback returning void media: media: intel/ipu6: Fix spelling mistake "remappinp" -> "remapping" media: intel/ipu6: explicitly include vmalloc.h media: cec.h: Fix kerneldoc media: uvcvideo: Refactor iterators media: v4l: async: refactor v4l2_async_create_ancillary_links media: intel/ipu6: Don't re-allocate memory for firmware media: dvb-frontends: tda10048: Fix integer overflow media: tc358746: Use the correct div_ function media: i2c: st-mipid02: Use the correct div function media: tegra-vde: Refactor timeout handling media: stk1160: Use min macro ...

Linus Torvalds committed 2 years ago

Commit 6fd600d2 parents 972a254 8771b7f307 files · +22908 −5428Browse files

Unified · Split

307 files
  1. Documentation/admin-guide/media/ipu6-isys.rst +161 −0
  2. Documentation/admin-guide/media/ipu6_isys_graph.svg +548 −0
  3. Documentation/admin-guide/media/mgb4.rst +15 −20
  4. Documentation/admin-guide/media/v4l-drivers.rst +1 −0
  5. Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml +127 −0
  6. Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml +1 −1
  7. Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml +1 −1
  8. Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml +25 −10
  9. Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml +1 −1
  10. Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml +1 −1
  11. Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml +4 −1
  12. Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml +1 −1
  13. Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml +1 −0
  14. Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml +11 −0
  15. Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml +512 −0
  16. Documentation/driver-api/media/drivers/index.rst +1 −0
  17. Documentation/driver-api/media/drivers/ipu6.rst +205 −0
  18. Documentation/userspace-api/media/cec/cec-func-open.rst +2 −2
  19. Documentation/userspace-api/media/dvb/frontend_f_open.rst +1 −1
  20. Documentation/userspace-api/media/glossary.rst +12 −0
  21. Documentation/userspace-api/media/v4l/dev-meta.rst +21 −0
  22. Documentation/userspace-api/media/v4l/dev-subdev.rst +28 −3
  23. Documentation/userspace-api/media/v4l/func-open.rst +2 −2
  24. Documentation/userspace-api/media/v4l/meta-formats.rst +2 −1
  25. Documentation/userspace-api/media/v4l/metafmt-generic.rst +340 −0
  26. Documentation/userspace-api/media/v4l/mmap.rst +1 −1
  27. Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst +48 −0
  28. Documentation/userspace-api/media/v4l/subdev-formats.rst +262 −7
  29. Documentation/userspace-api/media/v4l/user-func.rst +1 −0
  30. Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst +7 −0
  31. Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst +86 −0
  32. Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst +1 −0
  33. Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst +3 −3
  34. Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +33 −18
  35. Documentation/userspace-api/media/videodev2.h.rst.exceptions +1 −0
  36. MAINTAINERS +18 −1
  37. drivers/media/cec/core/cec-adap.c +20 −4
  38. drivers/media/cec/core/cec-api.c +4 −1
  39. drivers/media/cec/core/cec-core.c +2 −2
  40. drivers/media/cec/platform/cros-ec/cros-ec-cec.c +8 −1
  41. drivers/media/common/saa7146/saa7146_hlp.c +4 −4
  42. drivers/media/common/videobuf2/videobuf2-core.c +163 −68
  43. drivers/media/common/videobuf2/videobuf2-v4l2.c +28 −6
  44. drivers/media/dvb-core/dvbdev.c +1 −1
  45. drivers/media/dvb-frontends/as102_fe_types.h +1 −1
  46. drivers/media/dvb-frontends/cxd2880/Kconfig +1 −1
  47. drivers/media/dvb-frontends/drx39xyj/drx_driver.h +0 −2
  48. drivers/media/dvb-frontends/drx39xyj/drxj.c +26 −32
  49. drivers/media/dvb-frontends/lgdt3306a.c +5 −0
  50. drivers/media/dvb-frontends/m88ds3103.c +7 −0
  51. drivers/media/dvb-frontends/mxl5xx.c +11 −11
  52. drivers/media/dvb-frontends/si2165.c +2 −4
  53. drivers/media/dvb-frontends/stb0899_drv.c +1 −1
  54. drivers/media/dvb-frontends/tda10048.c +6 −3
  55. drivers/media/dvb-frontends/tda18271c2dd.c +2 −2
  56. drivers/media/i2c/Kconfig +2 −0
  57. drivers/media/i2c/adv7180.c +1 −1
  58. drivers/media/i2c/adv748x/adv748x-hdmi.c +8 −8
  59. drivers/media/i2c/adv7511-v4l2.c +11 −5
  60. drivers/media/i2c/adv7604.c +10 −10
  61. drivers/media/i2c/adv7842.c +17 −8
  62. drivers/media/i2c/dw9714.c +3 −3
  63. drivers/media/i2c/et8ek8/et8ek8_driver.c +2 −2
  64. drivers/media/i2c/hi556.c +93 −12
  65. drivers/media/i2c/imx214.c +1 −0
  66. drivers/media/i2c/imx219.c +35 −42
  67. drivers/media/i2c/imx335.c +304 −333
  68. drivers/media/i2c/max9271.h +5 −0
  69. drivers/media/i2c/ov2680.c +77 −26
  70. drivers/media/i2c/ov2740.c +14 −6
  71. drivers/media/i2c/ov4689.c +359 −314
  72. drivers/media/i2c/rdacm20.c +2 −2
  73. drivers/media/i2c/st-mipid02.c +1 −1
  74. drivers/media/i2c/tc358743.c +17 −8
  75. drivers/media/i2c/tc358746.c +1 −2
  76. drivers/media/i2c/tda1997x.c +7 −7
  77. drivers/media/i2c/ths7303.c +7 −3
  78. drivers/media/i2c/ths8200.c +10 −4
  79. drivers/media/i2c/tvp7002.c +21 −11
  80. drivers/media/mc/mc-devnode.c +2 −3
  81. drivers/media/mc/mc-entity.c +6 −0
  82. drivers/media/mmc/siano/smssdio.c +1 −24
  83. drivers/media/pci/cobalt/cobalt-v4l2.c +6 −6
  84. drivers/media/pci/intel/Kconfig +3 −1
  85. drivers/media/pci/intel/Makefile +1 −0
  86. drivers/media/pci/intel/ipu-bridge.c +47 −19
  87. drivers/media/pci/intel/ipu3/ipu3-cio2.c +9 −20
  88. drivers/media/pci/intel/ipu3/ipu3-cio2.h +0 −4
  89. drivers/media/pci/intel/ipu6/Kconfig +18 −0
  90. drivers/media/pci/intel/ipu6/Makefile +23 −0
  91. drivers/media/pci/intel/ipu6/ipu6-bus.c +165 −0
  92. drivers/media/pci/intel/ipu6/ipu6-bus.h +58 −0
  93. drivers/media/pci/intel/ipu6/ipu6-buttress.c +917 −0
  94. drivers/media/pci/intel/ipu6/ipu6-buttress.h +92 −0
  95. drivers/media/pci/intel/ipu6/ipu6-cpd.c +362 −0
  96. drivers/media/pci/intel/ipu6/ipu6-cpd.h +105 −0
  97. drivers/media/pci/intel/ipu6/ipu6-dma.c +502 −0
  98. drivers/media/pci/intel/ipu6/ipu6-dma.h +19 −0
  99. drivers/media/pci/intel/ipu6/ipu6-fw-com.c +413 −0
  100. drivers/media/pci/intel/ipu6/ipu6-fw-com.h +47 −0
  101. drivers/media/pci/intel/ipu6/ipu6-fw-isys.c +487 −0
  102. drivers/media/pci/intel/ipu6/ipu6-fw-isys.h +596 −0
  103. drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +663 −0
  104. drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +82 −0
  105. drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c +536 −0
  106. drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c +242 −0
  107. drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c +720 −0
  108. drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +810 −0
  109. drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +78 −0
  110. drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c +403 −0
  111. drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h +59 −0
  112. drivers/media/pci/intel/ipu6/ipu6-isys-video.c +1420 −0
  113. drivers/media/pci/intel/ipu6/ipu6-isys-video.h +141 −0
  114. drivers/media/pci/intel/ipu6/ipu6-isys.c +1367 −0
  115. drivers/media/pci/intel/ipu6/ipu6-isys.h +206 −0
  116. drivers/media/pci/intel/ipu6/ipu6-mmu.c +846 −0
  117. drivers/media/pci/intel/ipu6/ipu6-mmu.h +73 −0
  118. drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h +226 −0
  119. drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h +172 −0
  120. drivers/media/pci/intel/ipu6/ipu6-platform-regs.h +179 −0
  121. drivers/media/pci/intel/ipu6/ipu6.c +856 −0
  122. drivers/media/pci/intel/ipu6/ipu6.h +342 −0
  123. drivers/media/pci/intel/ivsc/mei_csi.c +18 −2
  124. drivers/media/pci/mgb4/mgb4_core.c +2 −2
  125. drivers/media/pci/mgb4/mgb4_regs.c +1 −1
  126. drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +1 −1
  127. drivers/media/pci/ngene/ngene-core.c +3 −1
  128. drivers/media/pci/saa7134/saa7134-alsa.c +2 −7
  129. drivers/media/pci/solo6x10/solo6x10-core.c +4 −12
  130. drivers/media/pci/ttpci/budget-av.c +286 −287
  131. drivers/media/pci/ttpci/budget-ci.c +247 −248
  132. drivers/media/pci/ttpci/budget-core.c +20 −18
  133. drivers/media/pci/ttpci/budget.c +93 −80
  134. drivers/media/pci/ttpci/budget.h +13 −8
  135. drivers/media/platform/Kconfig +1 −0
  136. drivers/media/platform/Makefile +1 −0
  137. drivers/media/platform/broadcom/Kconfig +23 −0
  138. drivers/media/platform/broadcom/Makefile +3 −0
  139. drivers/media/platform/broadcom/bcm2835-unicam-regs.h +246 −0
  140. drivers/media/platform/broadcom/bcm2835-unicam.c +2739 −0
  141. drivers/media/platform/cadence/cdns-csi2rx.c +14 −12
  142. drivers/media/platform/chips-media/wave5/wave5-helper.c +15 −2
  143. drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +12 −1
  144. drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +12 −1
  145. drivers/media/platform/chips-media/wave5/wave5-vpu.c +83 −42
  146. drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +4 −0
  147. drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +5 −5
  148. drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +3 −3
  149. drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h +1 −1
  150. drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c +3 −3
  151. drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c +2 −0
  152. drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c +11 −12
  153. drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +1 −1
  154. drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +2 −2
  155. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c +25 −17
  156. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c +6 −6
  157. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h +9 −6
  158. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c +7 −7
  159. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +3 −3
  160. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c +2 −2
  161. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c +2 −2
  162. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c +1 −1
  163. drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c +2 −2
  164. drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h +2 −2
  165. drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h +2 −2
  166. drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +1 −1
  167. drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c +3 −1
  168. drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h +1 −1
  169. drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c +3 −2
  170. drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c +4 −1
  171. drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h +1 −1
  172. drivers/media/platform/nvidia/tegra-vde/h264.c +3 −3
  173. drivers/media/platform/nxp/imx-mipi-csis.c +33 −1
  174. drivers/media/platform/qcom/camss/Makefile +1 −1
  175. drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +105 −3
  176. drivers/media/platform/qcom/camss/camss-csiphy.c +1 −0
  177. drivers/media/platform/qcom/camss/camss-vfe-17x.c +0 −0
  178. drivers/media/platform/qcom/camss/camss-vfe.c +20 −5
  179. drivers/media/platform/qcom/camss/camss-video.c +1 −0
  180. drivers/media/platform/qcom/camss/camss.c +307 −0
  181. drivers/media/platform/qcom/camss/camss.h +1 −0
  182. drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c +6 −3
  183. drivers/media/platform/renesas/rcar-vin/rcar-vin.h +1 −1
  184. drivers/media/platform/st/sti/c8sectpfe/Kconfig +0 −1
  185. drivers/media/platform/st/sti/c8sectpfe/Makefile +5 −2
  186. drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +1 −2
  187. drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +5 −0
  188. drivers/media/platform/st/sti/hva/hva-hw.c +1 −2
  189. drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +2 −5
  190. drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig +1 −0
  191. drivers/media/platform/ti/davinci/vpif_capture.c +2 −2
  192. drivers/media/platform/ti/davinci/vpif_display.c +1 −1
  193. drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +2 −3
  194. drivers/media/platform/verisilicon/hantro_h1_regs.h +2 −2
  195. drivers/media/platform/verisilicon/hantro_v4l2.c +1 −0
  196. drivers/media/radio/radio-shark2.c +1 −1
  197. drivers/media/rc/gpio-ir-recv.c +0 −1
  198. drivers/media/rc/imon.c +6 −6
  199. drivers/media/rc/ir-spi.c +22 −19
  200. drivers/media/rc/lirc_dev.c +9 −9
  201. drivers/media/spi/cxd2880-spi.c +1 −1
  202. drivers/media/spi/gs1662.c +18 −9
  203. drivers/media/test-drivers/vicodec/vicodec-core.c +1 −0
  204. drivers/media/test-drivers/vimc/vimc-capture.c +2 −1
  205. drivers/media/test-drivers/visl/visl-video.c +1 −0
  206. drivers/media/test-drivers/vivid/vivid-core.c +5 −4
  207. drivers/media/test-drivers/vivid/vivid-meta-out.c +0 −4
  208. drivers/media/test-drivers/vivid/vivid-touch-cap.c +0 −4
  209. drivers/media/tuners/xc5000.c +17 −22
  210. drivers/media/usb/as102/as102_usb_drv.c +1 −1
  211. drivers/media/usb/au0828/au0828-video.c +1 −4
  212. drivers/media/usb/b2c2/flexcop-usb.c +10 −7
  213. drivers/media/usb/dvb-usb-v2/af9035.c +9 −1
  214. drivers/media/usb/dvb-usb-v2/anysee.c +2 −2
  215. drivers/media/usb/dvb-usb/dib0700_devices.c +15 −3
  216. drivers/media/usb/dvb-usb/dw2102.c +242 −220
  217. drivers/media/usb/go7007/go7007-fw.c +2 −2
  218. drivers/media/usb/gspca/cpia1.c +2 −4
  219. drivers/media/usb/s2255/s2255drv.c +10 −10
  220. drivers/media/usb/siano/smsusb.c +14 −6
  221. drivers/media/usb/stk1160/stk1160-video.c +17 −13
  222. drivers/media/usb/uvc/uvc_ctrl.c +14 −12
  223. drivers/media/usb/uvc/uvc_driver.c +53 −1
  224. drivers/media/usb/uvc/uvcvideo.h +2 −0
  225. drivers/media/v4l2-core/v4l2-async.c +15 −15
  226. drivers/media/v4l2-core/v4l2-common.c +2 −0
  227. drivers/media/v4l2-core/v4l2-ctrls-api.c +19 −14
  228. drivers/media/v4l2-core/v4l2-ctrls-core.c +11 −13
  229. drivers/media/v4l2-core/v4l2-dev.c +6 −0
  230. drivers/media/v4l2-core/v4l2-device.c +7 −5
  231. drivers/media/v4l2-core/v4l2-i2c.c +1 −1
  232. drivers/media/v4l2-core/v4l2-ioctl.c +66 −7
  233. drivers/media/v4l2-core/v4l2-mem2mem.c +15 −0
  234. drivers/media/v4l2-core/v4l2-spi.c +1 −1
  235. drivers/media/v4l2-core/v4l2-subdev.c +85 −28
  236. drivers/staging/media/atomisp/Makefile +0 −1
  237. drivers/staging/media/atomisp/i2c/Kconfig +0 −15
  238. drivers/staging/media/atomisp/i2c/Makefile +0 −5
  239. drivers/staging/media/atomisp/i2c/atomisp-gc2235.c +1 −1
  240. drivers/staging/media/atomisp/i2c/atomisp-lm3554.c +0 −955
  241. drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +2 −2
  242. drivers/staging/media/atomisp/i2c/atomisp-ov2722.c +1 −13
  243. drivers/staging/media/atomisp/i2c/ov2722.h +0 −1
  244. drivers/staging/media/atomisp/include/linux/atomisp.h +0 −81
  245. drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h +1 −5
  246. drivers/staging/media/atomisp/include/linux/atomisp_platform.h +1 −40
  247. drivers/staging/media/atomisp/include/media/lm3554.h +0 −132
  248. drivers/staging/media/atomisp/pci/atomisp_cmd.c +125 −156
  249. drivers/staging/media/atomisp/pci/atomisp_cmd.h +9 −4
  250. drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +1 −30
  251. drivers/staging/media/atomisp/pci/atomisp_csi2.c +3 −23
  252. drivers/staging/media/atomisp/pci/atomisp_fops.c +1 −48
  253. drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c +32 −91
  254. drivers/staging/media/atomisp/pci/atomisp_internal.h +3 −16
  255. drivers/staging/media/atomisp/pci/atomisp_ioctl.c +40 −453
  256. drivers/staging/media/atomisp/pci/atomisp_subdev.c +52 −50
  257. drivers/staging/media/atomisp/pci/atomisp_subdev.h +1 −16
  258. drivers/staging/media/atomisp/pci/atomisp_tpg.c +0 −164
  259. drivers/staging/media/atomisp/pci/atomisp_tpg.h +0 −39
  260. drivers/staging/media/atomisp/pci/atomisp_v4l2.c +61 −122
  261. drivers/staging/media/atomisp/pci/bits.h +2 −2
  262. drivers/staging/media/atomisp/pci/defs.h +0 −37
  263. drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h +0 −1
  264. drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c +0 −38
  265. drivers/staging/media/atomisp/pci/hive_types.h +0 −19
  266. drivers/staging/media/atomisp/pci/ia_css.h +0 −1
  267. drivers/staging/media/atomisp/pci/ia_css_frame_public.h +0 −8
  268. drivers/staging/media/atomisp/pci/ia_css_stream_public.h +0 −17
  269. drivers/staging/media/atomisp/pci/ia_css_tpg.h +0 −79
  270. drivers/staging/media/atomisp/pci/isp2400_input_system_global.h +0 −1
  271. drivers/staging/media/atomisp/pci/isp2400_input_system_public.h +0 −15
  272. drivers/staging/media/atomisp/pci/isp2401_input_system_global.h +0 −1
  273. drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +2 −18
  274. drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c +0 −11
  275. drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +1 −27
  276. drivers/staging/media/atomisp/pci/sh_css.c +9 −129
  277. drivers/staging/media/atomisp/pci/sh_css_internal.h +0 −1
  278. drivers/staging/media/atomisp/pci/sh_css_mipi.c +1 −1
  279. drivers/staging/media/atomisp/pci/sh_css_sp.c +61 −66
  280. drivers/staging/media/atomisp/pci/sh_css_sp.h +0 −7
  281. drivers/staging/media/atomisp/pci/system_global.h +0 −12
  282. drivers/staging/media/imx/Kconfig +0 −1
  283. drivers/staging/media/ipu3/ipu3-css-fw.c +3 −1
  284. drivers/staging/media/ipu3/ipu3-css-fw.h +2 −0
  285. drivers/staging/media/ipu3/ipu3-css.c +0 −1
  286. drivers/staging/media/ipu3/ipu3.c +7 −3
  287. drivers/staging/media/max96712/max96712.c +1 −1
  288. drivers/staging/media/starfive/camss/stf-camss.c +8 −4
  289. drivers/staging/media/starfive/camss/stf-isp.c +2 −8
  290. drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c +1 −2
  291. drivers/staging/media/tegra-video/tegra20.c +3 −7
  292. drivers/staging/media/tegra-video/vi.c +6 −6
  293. include/media/cec.h +3 −1
  294. include/media/ipu6-pci-table.h +28 −0
  295. include/media/media-device.h +6 −0
  296. include/media/v4l2-async.h +3 −1
  297. include/media/v4l2-device.h +5 −2
  298. include/media/v4l2-ioctl.h +4 −0
  299. include/media/v4l2-mem2mem.h +2 −0
  300. include/media/v4l2-subdev.h +56 −14
  301. include/media/videobuf2-core.h +42 −10
  302. include/media/videobuf2-v4l2.h +2 −0
  303. include/uapi/linux/dvb/frontend.h +1 −1
  304. include/uapi/linux/media-bus-format.h +9 −0
  305. include/uapi/linux/v4l2-mediabus.h +12 −6
  306. include/uapi/linux/v4l2-subdev.h +11 −3
  307. include/uapi/linux/videodev2.h +44 −1
Documentation/admin-guide/media/ipu6-isys.rst+161−0
@@ -0,0 +1,161 @@
1+.. SPDX-License-Identifier: GPL-2.0
2+
3+.. include:: <isonum.txt>
4+
5+========================================================
6+Intel Image Processing Unit 6 (IPU6) Input System driver
7+========================================================
8+
9+Copyright |copy| 2023--2024 Intel Corporation
10+
11+Introduction
12+============
13+
14+This file documents the Intel IPU6 (6th generation Image Processing Unit)
15+Input System (MIPI CSI2 receiver) drivers located under
16+drivers/media/pci/intel/ipu6.
17+
18+The Intel IPU6 can be found in certain Intel SoCs but not in all SKUs:
19+
20+* Tiger Lake
21+* Jasper Lake
22+* Alder Lake
23+* Raptor Lake
24+* Meteor Lake
25+
26+Intel IPU6 is made up of two components - Input System (ISYS) and Processing
27+System (PSYS).
28+
29+The Input System mainly works as MIPI CSI-2 receiver which receives and
30+processes the image data from the sensors and outputs the frames to memory.
31+
32+There are 2 driver modules - intel-ipu6 and intel-ipu6-isys. intel-ipu6 is an
33+IPU6 common driver which does PCI configuration, firmware loading and parsing,
34+firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit)
35+configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2
36+sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected
37+to the IPU6 ISYS through V4L2 sub-device sensor drivers.
38+
39+.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more
40+ information about the IPU6 hardware.
41+
42+Input system driver
43+===================
44+
45+The Input System driver mainly configures CSI-2 D-PHY, constructs the firmware
46+stream configuration, sends commands to firmware, gets response from hardware
47+and firmware and then returns buffers to user. The ISYS is represented as
48+several V4L2 sub-devices as well as video nodes.
49+
50+.. kernel-figure:: ipu6_isys_graph.svg
51+ :alt: ipu6 isys media graph with multiple streams support
52+
53+ IPU6 ISYS media graph with multiple streams support
54+
55+The graph has been produced using the following command:
56+
57+.. code-block:: none
58+
59+ fdp -Gsplines=true -Tsvg < dot > dot.svg
60+
61+Capturing frames with IPU6 ISYS
62+-------------------------------
63+
64+IPU6 ISYS is used to capture frames from the camera sensors connected to the
65+CSI2 ports. The supported input formats of ISYS are listed in table below:
66+
67+.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}|
68+
69+.. flat-table::
70+ :header-rows: 1
71+
72+ * - IPU6 ISYS supported input formats
73+
74+ * - RGB565, RGB888
75+
76+ * - UYVY8, YUYV8
77+
78+ * - RAW8, RAW10, RAW12
79+
80+.. _ipu6_isys_capture_examples:
81+
82+Examples
83+~~~~~~~~
84+
85+Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this
86+machine, ov01a10 sensor is connected to IPU ISYS CSI-2 port 2, which can
87+generate images at sBGGR10 with resolution 1280x800.
88+
89+Using the media controller APIs, we can configure ov01a10 sensor by
90+media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS.
91+
92+.. code-block:: none
93+
94+ # Example 1 capture frame from ov01a10 camera sensor
95+ # This example assumes /dev/media0 as the IPU ISYS media device
96+ export MDEV=/dev/media0
97+
98+ # Establish the link for the media devices using media-ctl
99+ media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]"
100+
101+ # Set the format for the media devices
102+ media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]"
103+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]"
104+ media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]"
105+
106+Once the media pipeline is configured, desired sensor specific settings
107+(such as exposure and gain settings) can be set, using the yavta tool.
108+
109+e.g
110+
111+.. code-block:: none
112+
113+ # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36
114+ export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036")
115+
116+ yavta -w 0x009e0903 400 $SDEV
117+ yavta -w 0x009e0913 1000 $SDEV
118+ yavta -w 0x009e0911 2000 $SDEV
119+
120+Once the desired sensor settings are set, frame captures can be done as below.
121+
122+e.g
123+
124+.. code-block:: none
125+
126+ yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \
127+ -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0")
128+
129+With the above command, 10 frames are captured at 1280x800 resolution with
130+sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files.
131+
132+Here is another example of IPU6 ISYS RAW and metadata capture from camera
133+sensor ov2740 on Lenovo X1 Yoga laptop.
134+
135+.. code-block:: none
136+
137+ media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]"
138+ media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]"
139+ media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]"
140+
141+ # set routing
142+ media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]"
143+
144+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]"
145+ media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]"
146+ media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]"
147+ media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]"
148+
149+ CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0")
150+ ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \
151+ -f SGRBG10 ${CAPTURE_DEV}
152+
153+ CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1")
154+ ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \
155+ --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META}
156+
157+References
158+==========
159+
160+.. [#f1] https://git.ideasonboard.org/media-ctl.git
161+.. [#f2] https://git.ideasonboard.org/yavta.git
Documentation/admin-guide/media/ipu6_isys_graph.svg+548−0
@@ -0,0 +1,548 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4+<!-- Generated by graphviz version 2.43.0 (0)
5+ -->
6+<!-- Title: board Pages: 1 -->
7+<svg width="1703pt" height="1473pt"
8+ viewBox="0.00 0.00 1703.00 1473.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1469)">
10+<title>board</title>
11+<polygon fill="white" stroke="transparent" points="-4,4 -4,-1469 1699,-1469 1699,4 -4,4"/>
12+<!-- n00000001 -->
13+<g id="node1" class="node">
14+<title>n00000001</title>
15+<polygon fill="yellow" stroke="black" points="832.99,-750.08 629.99,-750.08 629.99,-712.08 832.99,-712.08 832.99,-750.08"/>
16+<text text-anchor="middle" x="731.49" y="-734.88" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text>
17+<text text-anchor="middle" x="731.49" y="-719.88" font-family="Times,serif" font-size="14.00">/dev/video0</text>
18+</g>
19+<!-- n00000005 -->
20+<g id="node2" class="node">
21+<title>n00000005</title>
22+<polygon fill="yellow" stroke="black" points="1396.59,-771.88 1193.59,-771.88 1193.59,-733.88 1396.59,-733.88 1396.59,-771.88"/>
23+<text text-anchor="middle" x="1295.09" y="-756.68" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text>
24+<text text-anchor="middle" x="1295.09" y="-741.68" font-family="Times,serif" font-size="14.00">/dev/video1</text>
25+</g>
26+<!-- n00000009 -->
27+<g id="node3" class="node">
28+<title>n00000009</title>
29+<polygon fill="yellow" stroke="black" points="1118.52,-690.47 915.52,-690.47 915.52,-652.47 1118.52,-652.47 1118.52,-690.47"/>
30+<text text-anchor="middle" x="1017.02" y="-675.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text>
31+<text text-anchor="middle" x="1017.02" y="-660.27" font-family="Times,serif" font-size="14.00">/dev/video2</text>
32+</g>
33+<!-- n0000000d -->
34+<g id="node4" class="node">
35+<title>n0000000d</title>
36+<polygon fill="yellow" stroke="black" points="1105.89,-838.84 902.89,-838.84 902.89,-800.84 1105.89,-800.84 1105.89,-838.84"/>
37+<text text-anchor="middle" x="1004.39" y="-823.64" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text>
38+<text text-anchor="middle" x="1004.39" y="-808.64" font-family="Times,serif" font-size="14.00">/dev/video3</text>
39+</g>
40+<!-- n00000011 -->
41+<g id="node5" class="node">
42+<title>n00000011</title>
43+<polygon fill="yellow" stroke="black" points="1279.22,-992.95 1076.22,-992.95 1076.22,-954.95 1279.22,-954.95 1279.22,-992.95"/>
44+<text text-anchor="middle" x="1177.72" y="-977.75" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 4</text>
45+<text text-anchor="middle" x="1177.72" y="-962.75" font-family="Times,serif" font-size="14.00">/dev/video4</text>
46+</g>
47+<!-- n00000015 -->
48+<g id="node6" class="node">
49+<title>n00000015</title>
50+<polygon fill="yellow" stroke="black" points="939.18,-984.91 736.18,-984.91 736.18,-946.91 939.18,-946.91 939.18,-984.91"/>
51+<text text-anchor="middle" x="837.68" y="-969.71" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 5</text>
52+<text text-anchor="middle" x="837.68" y="-954.71" font-family="Times,serif" font-size="14.00">/dev/video5</text>
53+</g>
54+<!-- n00000019 -->
55+<g id="node7" class="node">
56+<title>n00000019</title>
57+<polygon fill="yellow" stroke="black" points="957.87,-527.99 754.87,-527.99 754.87,-489.99 957.87,-489.99 957.87,-527.99"/>
58+<text text-anchor="middle" x="856.37" y="-512.79" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 6</text>
59+<text text-anchor="middle" x="856.37" y="-497.79" font-family="Times,serif" font-size="14.00">/dev/video6</text>
60+</g>
61+<!-- n0000001d -->
62+<g id="node8" class="node">
63+<title>n0000001d</title>
64+<polygon fill="yellow" stroke="black" points="1291.02,-542.15 1088.02,-542.15 1088.02,-504.15 1291.02,-504.15 1291.02,-542.15"/>
65+<text text-anchor="middle" x="1189.52" y="-526.95" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 7</text>
66+<text text-anchor="middle" x="1189.52" y="-511.95" font-family="Times,serif" font-size="14.00">/dev/video7</text>
67+</g>
68+<!-- n00000021 -->
69+<g id="node9" class="node">
70+<title>n00000021</title>
71+<polygon fill="yellow" stroke="black" points="202.74,-611.46 -0.26,-611.46 -0.26,-573.46 202.74,-573.46 202.74,-611.46"/>
72+<text text-anchor="middle" x="101.24" y="-596.26" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 8</text>
73+<text text-anchor="middle" x="101.24" y="-581.26" font-family="Times,serif" font-size="14.00">/dev/video8</text>
74+</g>
75+<!-- n00000025 -->
76+<g id="node10" class="node">
77+<title>n00000025</title>
78+<polygon fill="yellow" stroke="black" points="764.86,-637.89 561.86,-637.89 561.86,-599.89 764.86,-599.89 764.86,-637.89"/>
79+<text text-anchor="middle" x="663.36" y="-622.69" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 9</text>
80+<text text-anchor="middle" x="663.36" y="-607.69" font-family="Times,serif" font-size="14.00">/dev/video9</text>
81+</g>
82+<!-- n00000029 -->
83+<g id="node11" class="node">
84+<title>n00000029</title>
85+<polygon fill="yellow" stroke="black" points="358.62,-519.5 146.62,-519.5 146.62,-481.5 358.62,-481.5 358.62,-519.5"/>
86+<text text-anchor="middle" x="252.62" y="-504.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 10</text>
87+<text text-anchor="middle" x="252.62" y="-489.3" font-family="Times,serif" font-size="14.00">/dev/video10</text>
88+</g>
89+<!-- n0000002d -->
90+<g id="node12" class="node">
91+<title>n0000002d</title>
92+<polygon fill="yellow" stroke="black" points="481.4,-662.59 269.4,-662.59 269.4,-624.59 481.4,-624.59 481.4,-662.59"/>
93+<text text-anchor="middle" x="375.4" y="-647.39" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 11</text>
94+<text text-anchor="middle" x="375.4" y="-632.39" font-family="Times,serif" font-size="14.00">/dev/video11</text>
95+</g>
96+<!-- n00000031 -->
97+<g id="node13" class="node">
98+<title>n00000031</title>
99+<polygon fill="yellow" stroke="black" points="637.17,-837.47 425.17,-837.47 425.17,-799.47 637.17,-799.47 637.17,-837.47"/>
100+<text text-anchor="middle" x="531.17" y="-822.27" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 12</text>
101+<text text-anchor="middle" x="531.17" y="-807.27" font-family="Times,serif" font-size="14.00">/dev/video12</text>
102+</g>
103+<!-- n00000035 -->
104+<g id="node14" class="node">
105+<title>n00000035</title>
106+<polygon fill="yellow" stroke="black" points="337.75,-833.67 125.75,-833.67 125.75,-795.67 337.75,-795.67 337.75,-833.67"/>
107+<text text-anchor="middle" x="231.75" y="-818.47" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 13</text>
108+<text text-anchor="middle" x="231.75" y="-803.47" font-family="Times,serif" font-size="14.00">/dev/video13</text>
109+</g>
110+<!-- n00000039 -->
111+<g id="node15" class="node">
112+<title>n00000039</title>
113+<polygon fill="yellow" stroke="black" points="393.07,-317.96 181.07,-317.96 181.07,-279.96 393.07,-279.96 393.07,-317.96"/>
114+<text text-anchor="middle" x="287.07" y="-302.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 14</text>
115+<text text-anchor="middle" x="287.07" y="-287.76" font-family="Times,serif" font-size="14.00">/dev/video14</text>
116+</g>
117+<!-- n0000003d -->
118+<g id="node16" class="node">
119+<title>n0000003d</title>
120+<polygon fill="yellow" stroke="black" points="701.46,-391.04 489.46,-391.04 489.46,-353.04 701.46,-353.04 701.46,-391.04"/>
121+<text text-anchor="middle" x="595.46" y="-375.84" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 15</text>
122+<text text-anchor="middle" x="595.46" y="-360.84" font-family="Times,serif" font-size="14.00">/dev/video15</text>
123+</g>
124+<!-- n00000041 -->
125+<g id="node17" class="node">
126+<title>n00000041</title>
127+<polygon fill="yellow" stroke="black" points="212.45,-1228.8 0.45,-1228.8 0.45,-1190.8 212.45,-1190.8 212.45,-1228.8"/>
128+<text text-anchor="middle" x="106.45" y="-1213.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 16</text>
129+<text text-anchor="middle" x="106.45" y="-1198.6" font-family="Times,serif" font-size="14.00">/dev/video16</text>
130+</g>
131+<!-- n00000045 -->
132+<g id="node18" class="node">
133+<title>n00000045</title>
134+<polygon fill="yellow" stroke="black" points="784.86,-1252.38 572.86,-1252.38 572.86,-1214.38 784.86,-1214.38 784.86,-1252.38"/>
135+<text text-anchor="middle" x="678.86" y="-1237.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 17</text>
136+<text text-anchor="middle" x="678.86" y="-1222.18" font-family="Times,serif" font-size="14.00">/dev/video17</text>
137+</g>
138+<!-- n00000049 -->
139+<g id="node19" class="node">
140+<title>n00000049</title>
141+<polygon fill="yellow" stroke="black" points="503.14,-1169.96 291.14,-1169.96 291.14,-1131.96 503.14,-1131.96 503.14,-1169.96"/>
142+<text text-anchor="middle" x="397.14" y="-1154.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 18</text>
143+<text text-anchor="middle" x="397.14" y="-1139.76" font-family="Times,serif" font-size="14.00">/dev/video18</text>
144+</g>
145+<!-- n0000004d -->
146+<g id="node20" class="node">
147+<title>n0000004d</title>
148+<polygon fill="yellow" stroke="black" points="492.62,-1319.4 280.62,-1319.4 280.62,-1281.4 492.62,-1281.4 492.62,-1319.4"/>
149+<text text-anchor="middle" x="386.62" y="-1304.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 19</text>
150+<text text-anchor="middle" x="386.62" y="-1289.2" font-family="Times,serif" font-size="14.00">/dev/video19</text>
151+</g>
152+<!-- n00000051 -->
153+<g id="node21" class="node">
154+<title>n00000051</title>
155+<polygon fill="yellow" stroke="black" points="680.74,-1464.66 468.74,-1464.66 468.74,-1426.66 680.74,-1426.66 680.74,-1464.66"/>
156+<text text-anchor="middle" x="574.74" y="-1449.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 20</text>
157+<text text-anchor="middle" x="574.74" y="-1434.46" font-family="Times,serif" font-size="14.00">/dev/video20</text>
158+</g>
159+<!-- n00000055 -->
160+<g id="node22" class="node">
161+<title>n00000055</title>
162+<polygon fill="yellow" stroke="black" points="302.42,-1452.56 90.42,-1452.56 90.42,-1414.56 302.42,-1414.56 302.42,-1452.56"/>
163+<text text-anchor="middle" x="196.42" y="-1437.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 21</text>
164+<text text-anchor="middle" x="196.42" y="-1422.36" font-family="Times,serif" font-size="14.00">/dev/video21</text>
165+</g>
166+<!-- n00000059 -->
167+<g id="node23" class="node">
168+<title>n00000059</title>
169+<polygon fill="yellow" stroke="black" points="319.89,-1018.32 107.89,-1018.32 107.89,-980.32 319.89,-980.32 319.89,-1018.32"/>
170+<text text-anchor="middle" x="213.89" y="-1003.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 22</text>
171+<text text-anchor="middle" x="213.89" y="-988.12" font-family="Times,serif" font-size="14.00">/dev/video22</text>
172+</g>
173+<!-- n0000005d -->
174+<g id="node24" class="node">
175+<title>n0000005d</title>
176+<polygon fill="yellow" stroke="black" points="692.62,-1031.39 480.62,-1031.39 480.62,-993.39 692.62,-993.39 692.62,-1031.39"/>
177+<text text-anchor="middle" x="586.62" y="-1016.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 23</text>
178+<text text-anchor="middle" x="586.62" y="-1001.19" font-family="Times,serif" font-size="14.00">/dev/video23</text>
179+</g>
180+<!-- n00000061 -->
181+<g id="node25" class="node">
182+<title>n00000061</title>
183+<polygon fill="yellow" stroke="black" points="1122.45,-248.8 910.45,-248.8 910.45,-210.8 1122.45,-210.8 1122.45,-248.8"/>
184+<text text-anchor="middle" x="1016.45" y="-233.6" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 24</text>
185+<text text-anchor="middle" x="1016.45" y="-218.6" font-family="Times,serif" font-size="14.00">/dev/video24</text>
186+</g>
187+<!-- n00000065 -->
188+<g id="node26" class="node">
189+<title>n00000065</title>
190+<polygon fill="yellow" stroke="black" points="1694.86,-272.38 1482.86,-272.38 1482.86,-234.38 1694.86,-234.38 1694.86,-272.38"/>
191+<text text-anchor="middle" x="1588.86" y="-257.18" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 25</text>
192+<text text-anchor="middle" x="1588.86" y="-242.18" font-family="Times,serif" font-size="14.00">/dev/video25</text>
193+</g>
194+<!-- n00000069 -->
195+<g id="node27" class="node">
196+<title>n00000069</title>
197+<polygon fill="yellow" stroke="black" points="1413.14,-189.96 1201.14,-189.96 1201.14,-151.96 1413.14,-151.96 1413.14,-189.96"/>
198+<text text-anchor="middle" x="1307.14" y="-174.76" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 26</text>
199+<text text-anchor="middle" x="1307.14" y="-159.76" font-family="Times,serif" font-size="14.00">/dev/video26</text>
200+</g>
201+<!-- n0000006d -->
202+<g id="node28" class="node">
203+<title>n0000006d</title>
204+<polygon fill="yellow" stroke="black" points="1402.62,-339.4 1190.62,-339.4 1190.62,-301.4 1402.62,-301.4 1402.62,-339.4"/>
205+<text text-anchor="middle" x="1296.62" y="-324.2" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 27</text>
206+<text text-anchor="middle" x="1296.62" y="-309.2" font-family="Times,serif" font-size="14.00">/dev/video27</text>
207+</g>
208+<!-- n00000071 -->
209+<g id="node29" class="node">
210+<title>n00000071</title>
211+<polygon fill="yellow" stroke="black" points="1590.74,-484.66 1378.74,-484.66 1378.74,-446.66 1590.74,-446.66 1590.74,-484.66"/>
212+<text text-anchor="middle" x="1484.74" y="-469.46" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 28</text>
213+<text text-anchor="middle" x="1484.74" y="-454.46" font-family="Times,serif" font-size="14.00">/dev/video28</text>
214+</g>
215+<!-- n00000075 -->
216+<g id="node30" class="node">
217+<title>n00000075</title>
218+<polygon fill="yellow" stroke="black" points="1212.42,-472.56 1000.42,-472.56 1000.42,-434.56 1212.42,-434.56 1212.42,-472.56"/>
219+<text text-anchor="middle" x="1106.42" y="-457.36" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 29</text>
220+<text text-anchor="middle" x="1106.42" y="-442.36" font-family="Times,serif" font-size="14.00">/dev/video29</text>
221+</g>
222+<!-- n00000079 -->
223+<g id="node31" class="node">
224+<title>n00000079</title>
225+<polygon fill="yellow" stroke="black" points="1229.89,-38.32 1017.89,-38.32 1017.89,-0.32 1229.89,-0.32 1229.89,-38.32"/>
226+<text text-anchor="middle" x="1123.89" y="-23.12" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 30</text>
227+<text text-anchor="middle" x="1123.89" y="-8.12" font-family="Times,serif" font-size="14.00">/dev/video30</text>
228+</g>
229+<!-- n0000007d -->
230+<g id="node32" class="node">
231+<title>n0000007d</title>
232+<polygon fill="yellow" stroke="black" points="1602.62,-51.39 1390.62,-51.39 1390.62,-13.39 1602.62,-13.39 1602.62,-51.39"/>
233+<text text-anchor="middle" x="1496.62" y="-36.19" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 31</text>
234+<text text-anchor="middle" x="1496.62" y="-21.19" font-family="Times,serif" font-size="14.00">/dev/video31</text>
235+</g>
236+<!-- n00000081 -->
237+<g id="node33" class="node">
238+<title>n00000081</title>
239+<path fill="green" stroke="black" d="M924.28,-700.28C924.28,-700.28 1108.28,-700.28 1108.28,-700.28 1114.28,-700.28 1120.28,-706.28 1120.28,-712.28 1120.28,-712.28 1120.28,-772.28 1120.28,-772.28 1120.28,-778.28 1114.28,-784.28 1108.28,-784.28 1108.28,-784.28 924.28,-784.28 924.28,-784.28 918.28,-784.28 912.28,-778.28 912.28,-772.28 912.28,-772.28 912.28,-712.28 912.28,-712.28 912.28,-706.28 918.28,-700.28 924.28,-700.28"/>
240+<text text-anchor="middle" x="1016.28" y="-769.08" font-family="Times,serif" font-size="14.00">0</text>
241+<polyline fill="none" stroke="black" points="912.28,-761.28 1120.28,-761.28 "/>
242+<text text-anchor="middle" x="1016.28" y="-746.08" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text>
243+<text text-anchor="middle" x="1016.28" y="-731.08" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev0</text>
244+<polyline fill="none" stroke="black" points="912.28,-723.28 1120.28,-723.28 "/>
245+<text text-anchor="middle" x="925.28" y="-708.08" font-family="Times,serif" font-size="14.00">1</text>
246+<polyline fill="none" stroke="black" points="938.28,-700.28 938.28,-723.28 "/>
247+<text text-anchor="middle" x="951.28" y="-708.08" font-family="Times,serif" font-size="14.00">2</text>
248+<polyline fill="none" stroke="black" points="964.28,-700.28 964.28,-723.28 "/>
249+<text text-anchor="middle" x="977.28" y="-708.08" font-family="Times,serif" font-size="14.00">3</text>
250+<polyline fill="none" stroke="black" points="990.28,-700.28 990.28,-723.28 "/>
251+<text text-anchor="middle" x="1003.28" y="-708.08" font-family="Times,serif" font-size="14.00">4</text>
252+<polyline fill="none" stroke="black" points="1016.28,-700.28 1016.28,-723.28 "/>
253+<text text-anchor="middle" x="1029.28" y="-708.08" font-family="Times,serif" font-size="14.00">5</text>
254+<polyline fill="none" stroke="black" points="1042.28,-700.28 1042.28,-723.28 "/>
255+<text text-anchor="middle" x="1055.28" y="-708.08" font-family="Times,serif" font-size="14.00">6</text>
256+<polyline fill="none" stroke="black" points="1068.28,-700.28 1068.28,-723.28 "/>
257+<text text-anchor="middle" x="1081.28" y="-708.08" font-family="Times,serif" font-size="14.00">7</text>
258+<polyline fill="none" stroke="black" points="1094.28,-700.28 1094.28,-723.28 "/>
259+<text text-anchor="middle" x="1107.28" y="-708.08" font-family="Times,serif" font-size="14.00">8</text>
260+</g>
261+<!-- n00000081&#45;&gt;n00000001 -->
262+<g id="edge1" class="edge">
263+<title>n00000081:port1&#45;&gt;n00000001</title>
264+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M912.28,-711.28C912.28,-711.28 880.33,-714.78 843.28,-718.84"/>
265+<polygon fill="black" stroke="black" points="842.81,-715.37 833.25,-719.94 843.57,-722.33 842.81,-715.37"/>
266+</g>
267+<!-- n00000081&#45;&gt;n00000005 -->
268+<g id="edge2" class="edge">
269+<title>n00000081:port2&#45;&gt;n00000005</title>
270+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M951.38,-700.28C951.38,-700.28 1086.18,-688.61 1123.48,-697.08 1155.93,-704.45 1158.99,-719.67 1190.39,-730.68 1190.49,-730.71 1190.59,-730.75 1190.69,-730.78"/>
271+<polygon fill="black" stroke="black" points="1189.45,-734.06 1200.05,-733.86 1191.64,-727.41 1189.45,-734.06"/>
272+</g>
273+<!-- n00000081&#45;&gt;n00000009 -->
274+<g id="edge3" class="edge">
275+<title>n00000081:port3&#45;&gt;n00000009</title>
276+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M977.28,-700.28C977.28,-700.28 979.31,-698.81 982.45,-696.54"/>
277+<polygon fill="black" stroke="black" points="984.7,-699.23 990.74,-690.53 980.59,-693.56 984.7,-699.23"/>
278+</g>
279+<!-- n00000081&#45;&gt;n0000000d -->
280+<g id="edge4" class="edge">
281+<title>n00000081:port4&#45;&gt;n0000000d</title>
282+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1003.38,-700.26C1003.38,-700.26 916.62,-689.8 909.08,-697.08 880.2,-725.01 885.68,-754.82 909.08,-787.48 910.88,-789.99 918.96,-793.59 929.7,-797.47"/>
283+<polygon fill="black" stroke="black" points="928.69,-800.82 939.28,-800.79 930.98,-794.21 928.69,-800.82"/>
284+</g>
285+<!-- n00000081&#45;&gt;n00000011 -->
286+<g id="edge5" class="edge">
287+<title>n00000081:port5&#45;&gt;n00000011</title>
288+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1029.19,-700.26C1029.19,-700.26 1115.28,-690.56 1123.48,-697.08 1198.37,-756.64 1190.55,-886.51 1182.64,-944.71"/>
289+<polygon fill="black" stroke="black" points="1179.16,-944.31 1181.18,-954.71 1186.09,-945.32 1179.16,-944.31"/>
290+</g>
291+<!-- n00000081&#45;&gt;n00000015 -->
292+<g id="edge6" class="edge">
293+<title>n00000081:port6&#45;&gt;n00000015</title>
294+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1055.18,-700.28C1055.18,-700.28 915.57,-692.2 909.08,-697.08 834.02,-753.51 831.79,-879.34 835.06,-936.56"/>
295+<polygon fill="black" stroke="black" points="831.58,-936.99 835.74,-946.73 838.56,-936.52 831.58,-936.99"/>
296+</g>
297+<!-- n00000081&#45;&gt;n00000019 -->
298+<g id="edge7" class="edge">
299+<title>n00000081:port7&#45;&gt;n00000019</title>
300+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1081.28,-700.28C1081.28,-700.28 916.04,-696.54 912.32,-693.67 864.52,-656.73 856.3,-580.22 855.62,-538.2"/>
301+<polygon fill="black" stroke="black" points="859.11,-538.05 855.59,-528.06 852.11,-538.07 859.11,-538.05"/>
302+</g>
303+<!-- n00000081&#45;&gt;n0000001d -->
304+<g id="edge8" class="edge">
305+<title>n00000081:port8&#45;&gt;n0000001d</title>
306+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1107.28,-700.28C1107.28,-700.28 1119.29,-696.23 1121.72,-693.67 1159.76,-653.62 1177.38,-589.6 1184.78,-552.46"/>
307+<polygon fill="black" stroke="black" points="1188.29,-552.76 1186.69,-542.29 1181.41,-551.47 1188.29,-552.76"/>
308+</g>
309+<!-- n0000008b -->
310+<g id="node34" class="node">
311+<title>n0000008b</title>
312+<path fill="green" stroke="black" d="M293.1,-532.08C293.1,-532.08 477.1,-532.08 477.1,-532.08 483.1,-532.08 489.1,-538.08 489.1,-544.08 489.1,-544.08 489.1,-604.08 489.1,-604.08 489.1,-610.08 483.1,-616.08 477.1,-616.08 477.1,-616.08 293.1,-616.08 293.1,-616.08 287.1,-616.08 281.1,-610.08 281.1,-604.08 281.1,-604.08 281.1,-544.08 281.1,-544.08 281.1,-538.08 287.1,-532.08 293.1,-532.08"/>
313+<text text-anchor="middle" x="385.1" y="-600.88" font-family="Times,serif" font-size="14.00">0</text>
314+<polyline fill="none" stroke="black" points="281.1,-593.08 489.1,-593.08 "/>
315+<text text-anchor="middle" x="385.1" y="-577.88" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text>
316+<text text-anchor="middle" x="385.1" y="-562.88" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev1</text>
317+<polyline fill="none" stroke="black" points="281.1,-555.08 489.1,-555.08 "/>
318+<text text-anchor="middle" x="294.1" y="-539.88" font-family="Times,serif" font-size="14.00">1</text>
319+<polyline fill="none" stroke="black" points="307.1,-532.08 307.1,-555.08 "/>
320+<text text-anchor="middle" x="320.1" y="-539.88" font-family="Times,serif" font-size="14.00">2</text>
321+<polyline fill="none" stroke="black" points="333.1,-532.08 333.1,-555.08 "/>
322+<text text-anchor="middle" x="346.1" y="-539.88" font-family="Times,serif" font-size="14.00">3</text>
323+<polyline fill="none" stroke="black" points="359.1,-532.08 359.1,-555.08 "/>
324+<text text-anchor="middle" x="372.1" y="-539.88" font-family="Times,serif" font-size="14.00">4</text>
325+<polyline fill="none" stroke="black" points="385.1,-532.08 385.1,-555.08 "/>
326+<text text-anchor="middle" x="398.1" y="-539.88" font-family="Times,serif" font-size="14.00">5</text>
327+<polyline fill="none" stroke="black" points="411.1,-532.08 411.1,-555.08 "/>
328+<text text-anchor="middle" x="424.1" y="-539.88" font-family="Times,serif" font-size="14.00">6</text>
329+<polyline fill="none" stroke="black" points="437.1,-532.08 437.1,-555.08 "/>
330+<text text-anchor="middle" x="450.1" y="-539.88" font-family="Times,serif" font-size="14.00">7</text>
331+<polyline fill="none" stroke="black" points="463.1,-532.08 463.1,-555.08 "/>
332+<text text-anchor="middle" x="476.1" y="-539.88" font-family="Times,serif" font-size="14.00">8</text>
333+</g>
334+<!-- n0000008b&#45;&gt;n00000021 -->
335+<g id="edge9" class="edge">
336+<title>n0000008b:port1&#45;&gt;n00000021</title>
337+<path fill="none" stroke="black" d="M281.1,-543.08C281.1,-543.08 240.1,-560.51 205.94,-570.26 205.35,-570.43 204.77,-570.59 204.18,-570.76"/>
338+<polygon fill="black" stroke="black" points="203.2,-567.39 194.47,-573.39 205.03,-574.15 203.2,-567.39"/>
339+</g>
340+<!-- n0000008b&#45;&gt;n00000025 -->
341+<g id="edge10" class="edge">
342+<title>n0000008b:port2&#45;&gt;n00000025</title>
343+<path fill="none" stroke="black" d="M320.2,-532.07C320.2,-532.07 456.9,-514.37 492.3,-528.88 528.42,-543.68 522.86,-571.78 556.11,-594.53"/>
344+<polygon fill="black" stroke="black" points="554.54,-597.67 564.9,-599.88 558.18,-591.69 554.54,-597.67"/>
345+</g>
346+<!-- n0000008b&#45;&gt;n00000029 -->
347+<g id="edge11" class="edge">
348+<title>n0000008b:port3&#45;&gt;n00000029</title>
349+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M346.1,-532.08C346.1,-532.08 333.93,-527.96 318.37,-522.71"/>
350+<polygon fill="black" stroke="black" points="319.48,-519.39 308.88,-519.5 317.24,-526.02 319.48,-519.39"/>
351+</g>
352+<!-- n0000008b&#45;&gt;n0000002d -->
353+<g id="edge12" class="edge">
354+<title>n0000008b:port4&#45;&gt;n0000002d</title>
355+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M372.19,-532.05C372.19,-532.05 292.97,-514.3 277.9,-528.88 249.01,-556.8 253.16,-587.62 277.9,-619.28 278.34,-619.85 280.33,-620.69 283.45,-621.71"/>
356+<polygon fill="black" stroke="black" points="282.71,-625.14 293.29,-624.58 284.67,-618.42 282.71,-625.14"/>
357+</g>
358+<!-- n0000008b&#45;&gt;n00000031 -->
359+<g id="edge13" class="edge">
360+<title>n0000008b:port5&#45;&gt;n00000031</title>
361+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M398,-532.05C398,-532.05 476.28,-515.34 492.3,-528.88 568.49,-593.29 550.55,-729.67 538.14,-789.41"/>
362+<polygon fill="black" stroke="black" points="534.69,-788.79 535.99,-799.31 541.53,-790.28 534.69,-788.79"/>
363+</g>
364+<!-- n0000008b&#45;&gt;n00000035 -->
365+<g id="edge14" class="edge">
366+<title>n0000008b:port6&#45;&gt;n00000035</title>
367+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M424,-532.07C424,-532.07 290.37,-518.48 277.9,-528.88 202.27,-591.86 215.34,-725.69 225.66,-785.15"/>
368+<polygon fill="black" stroke="black" points="222.29,-786.14 227.54,-795.35 229.17,-784.88 222.29,-786.14"/>
369+</g>
370+<!-- n0000008b&#45;&gt;n00000039 -->
371+<g id="edge15" class="edge">
372+<title>n0000008b:port7&#45;&gt;n00000039</title>
373+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M450.1,-532.08C450.1,-532.08 395.22,-528.13 383.45,-518.65 375.46,-512.21 322.64,-385.46 298.76,-327.47"/>
374+<polygon fill="black" stroke="black" points="301.96,-326.05 294.92,-318.14 295.49,-328.72 301.96,-326.05"/>
375+</g>
376+<!-- n0000008b&#45;&gt;n0000003d -->
377+<g id="edge16" class="edge">
378+<title>n0000008b:port8&#45;&gt;n0000003d</title>
379+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M476.1,-532.08C476.1,-532.08 522.37,-522.39 526.85,-518.65 563.15,-488.33 581.38,-434.52 589.6,-401.2"/>
380+<polygon fill="black" stroke="black" points="593.08,-401.69 591.93,-391.16 586.26,-400.11 593.08,-401.69"/>
381+</g>
382+<!-- n00000095 -->
383+<g id="node35" class="node">
384+<title>n00000095</title>
385+<path fill="green" stroke="black" d="M301.38,-1180.11C301.38,-1180.11 485.38,-1180.11 485.38,-1180.11 491.38,-1180.11 497.38,-1186.11 497.38,-1192.11 497.38,-1192.11 497.38,-1252.11 497.38,-1252.11 497.38,-1258.11 491.38,-1264.11 485.38,-1264.11 485.38,-1264.11 301.38,-1264.11 301.38,-1264.11 295.38,-1264.11 289.38,-1258.11 289.38,-1252.11 289.38,-1252.11 289.38,-1192.11 289.38,-1192.11 289.38,-1186.11 295.38,-1180.11 301.38,-1180.11"/>
386+<text text-anchor="middle" x="393.38" y="-1248.91" font-family="Times,serif" font-size="14.00">0</text>
387+<polyline fill="none" stroke="black" points="289.38,-1241.11 497.38,-1241.11 "/>
388+<text text-anchor="middle" x="393.38" y="-1225.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text>
389+<text text-anchor="middle" x="393.38" y="-1210.91" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev2</text>
390+<polyline fill="none" stroke="black" points="289.38,-1203.11 497.38,-1203.11 "/>
391+<text text-anchor="middle" x="302.38" y="-1187.91" font-family="Times,serif" font-size="14.00">1</text>
392+<polyline fill="none" stroke="black" points="315.38,-1180.11 315.38,-1203.11 "/>
393+<text text-anchor="middle" x="328.38" y="-1187.91" font-family="Times,serif" font-size="14.00">2</text>
394+<polyline fill="none" stroke="black" points="341.38,-1180.11 341.38,-1203.11 "/>
395+<text text-anchor="middle" x="354.38" y="-1187.91" font-family="Times,serif" font-size="14.00">3</text>
396+<polyline fill="none" stroke="black" points="367.38,-1180.11 367.38,-1203.11 "/>
397+<text text-anchor="middle" x="380.38" y="-1187.91" font-family="Times,serif" font-size="14.00">4</text>
398+<polyline fill="none" stroke="black" points="393.38,-1180.11 393.38,-1203.11 "/>
399+<text text-anchor="middle" x="406.38" y="-1187.91" font-family="Times,serif" font-size="14.00">5</text>
400+<polyline fill="none" stroke="black" points="419.38,-1180.11 419.38,-1203.11 "/>
401+<text text-anchor="middle" x="432.38" y="-1187.91" font-family="Times,serif" font-size="14.00">6</text>
402+<polyline fill="none" stroke="black" points="445.38,-1180.11 445.38,-1203.11 "/>
403+<text text-anchor="middle" x="458.38" y="-1187.91" font-family="Times,serif" font-size="14.00">7</text>
404+<polyline fill="none" stroke="black" points="471.38,-1180.11 471.38,-1203.11 "/>
405+<text text-anchor="middle" x="484.38" y="-1187.91" font-family="Times,serif" font-size="14.00">8</text>
406+</g>
407+<!-- n00000095&#45;&gt;n00000041 -->
408+<g id="edge17" class="edge">
409+<title>n00000095:port1&#45;&gt;n00000041</title>
410+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M289.38,-1191.11C289.38,-1191.11 258.94,-1194.22 222.89,-1197.91"/>
411+<polygon fill="black" stroke="black" points="222.19,-1194.46 212.6,-1198.96 222.9,-1201.42 222.19,-1194.46"/>
412+</g>
413+<!-- n00000095&#45;&gt;n00000045 -->
414+<g id="edge18" class="edge">
415+<title>n00000095:port2&#45;&gt;n00000045</title>
416+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M328.48,-1180.11C328.48,-1180.11 463.26,-1168.53 500.58,-1176.91 534.02,-1184.43 537.24,-1200.06 569.66,-1211.18 569.76,-1211.22 569.86,-1211.25 569.96,-1211.29"/>
417+<polygon fill="black" stroke="black" points="568.86,-1214.61 579.45,-1214.34 571,-1207.95 568.86,-1214.61"/>
418+</g>
419+<!-- n00000095&#45;&gt;n00000049 -->
420+<g id="edge19" class="edge">
421+<title>n00000095:port3&#45;&gt;n00000049</title>
422+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M354.38,-1180.11C354.38,-1180.11 356.8,-1178.46 360.49,-1175.94"/>
423+<polygon fill="black" stroke="black" points="362.56,-1178.77 368.86,-1170.24 358.62,-1172.98 362.56,-1178.77"/>
424+</g>
425+<!-- n00000095&#45;&gt;n0000004d -->
426+<g id="edge20" class="edge">
427+<title>n00000095:port4&#45;&gt;n0000004d</title>
428+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M380.47,-1180.09C380.47,-1180.09 293.71,-1169.63 286.18,-1176.91 257.29,-1204.84 262.63,-1234.76 286.18,-1267.31 288.16,-1270.05 297.33,-1273.96 309.38,-1278.13"/>
429+<polygon fill="black" stroke="black" points="308.49,-1281.53 319.09,-1281.36 310.7,-1274.88 308.49,-1281.53"/>
430+</g>
431+<!-- n00000095&#45;&gt;n00000051 -->
432+<g id="edge21" class="edge">
433+<title>n00000095:port5&#45;&gt;n00000051</title>
434+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M406.28,-1180.09C406.28,-1180.09 492.13,-1170.7 500.58,-1176.91 576.41,-1232.66 579.83,-1358.79 577.09,-1416.2"/>
435+<polygon fill="black" stroke="black" points="573.59,-1416.23 576.51,-1426.41 580.58,-1416.63 573.59,-1416.23"/>
436+</g>
437+<!-- n00000095&#45;&gt;n00000055 -->
438+<g id="edge22" class="edge">
439+<title>n00000095:port6&#45;&gt;n00000055</title>
440+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M432.28,-1180.11C432.28,-1180.11 292.85,-1172.29 286.18,-1176.91 211.26,-1228.86 198.3,-1348.49 196.45,-1404.12"/>
441+<polygon fill="black" stroke="black" points="192.94,-1404.28 196.21,-1414.36 199.94,-1404.44 192.94,-1404.28"/>
442+</g>
443+<!-- n00000095&#45;&gt;n00000059 -->
444+<g id="edge23" class="edge">
445+<title>n00000095:port7&#45;&gt;n00000059</title>
446+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M458.38,-1180.11C458.38,-1180.11 291.84,-1175.85 287.94,-1173.16 239.87,-1139.96 222.85,-1068.83 216.94,-1028.6"/>
447+<polygon fill="black" stroke="black" points="220.39,-1028.06 215.6,-1018.61 213.46,-1028.98 220.39,-1028.06"/>
448+</g>
449+<!-- n00000095&#45;&gt;n0000005d -->
450+<g id="edge24" class="edge">
451+<title>n00000095:port8&#45;&gt;n0000005d</title>
452+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M484.38,-1180.11C484.38,-1180.11 502.45,-1176.49 506.34,-1173.16 547.25,-1138.2 569.47,-1077.38 579.62,-1041.41"/>
453+<polygon fill="black" stroke="black" points="583.06,-1042.09 582.28,-1031.53 576.3,-1040.27 583.06,-1042.09"/>
454+</g>
455+<!-- n0000009f -->
456+<g id="node36" class="node">
457+<title>n0000009f</title>
458+<path fill="green" stroke="black" d="M1211.38,-200.11C1211.38,-200.11 1395.38,-200.11 1395.38,-200.11 1401.38,-200.11 1407.38,-206.11 1407.38,-212.11 1407.38,-212.11 1407.38,-272.11 1407.38,-272.11 1407.38,-278.11 1401.38,-284.11 1395.38,-284.11 1395.38,-284.11 1211.38,-284.11 1211.38,-284.11 1205.38,-284.11 1199.38,-278.11 1199.38,-272.11 1199.38,-272.11 1199.38,-212.11 1199.38,-212.11 1199.38,-206.11 1205.38,-200.11 1211.38,-200.11"/>
459+<text text-anchor="middle" x="1303.38" y="-268.91" font-family="Times,serif" font-size="14.00">0</text>
460+<polyline fill="none" stroke="black" points="1199.38,-261.11 1407.38,-261.11 "/>
461+<text text-anchor="middle" x="1303.38" y="-245.91" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text>
462+<text text-anchor="middle" x="1303.38" y="-230.91" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev3</text>
463+<polyline fill="none" stroke="black" points="1199.38,-223.11 1407.38,-223.11 "/>
464+<text text-anchor="middle" x="1212.38" y="-207.91" font-family="Times,serif" font-size="14.00">1</text>
465+<polyline fill="none" stroke="black" points="1225.38,-200.11 1225.38,-223.11 "/>
466+<text text-anchor="middle" x="1238.38" y="-207.91" font-family="Times,serif" font-size="14.00">2</text>
467+<polyline fill="none" stroke="black" points="1251.38,-200.11 1251.38,-223.11 "/>
468+<text text-anchor="middle" x="1264.38" y="-207.91" font-family="Times,serif" font-size="14.00">3</text>
469+<polyline fill="none" stroke="black" points="1277.38,-200.11 1277.38,-223.11 "/>
470+<text text-anchor="middle" x="1290.38" y="-207.91" font-family="Times,serif" font-size="14.00">4</text>
471+<polyline fill="none" stroke="black" points="1303.38,-200.11 1303.38,-223.11 "/>
472+<text text-anchor="middle" x="1316.38" y="-207.91" font-family="Times,serif" font-size="14.00">5</text>
473+<polyline fill="none" stroke="black" points="1329.38,-200.11 1329.38,-223.11 "/>
474+<text text-anchor="middle" x="1342.38" y="-207.91" font-family="Times,serif" font-size="14.00">6</text>
475+<polyline fill="none" stroke="black" points="1355.38,-200.11 1355.38,-223.11 "/>
476+<text text-anchor="middle" x="1368.38" y="-207.91" font-family="Times,serif" font-size="14.00">7</text>
477+<polyline fill="none" stroke="black" points="1381.38,-200.11 1381.38,-223.11 "/>
478+<text text-anchor="middle" x="1394.38" y="-207.91" font-family="Times,serif" font-size="14.00">8</text>
479+</g>
480+<!-- n0000009f&#45;&gt;n00000061 -->
481+<g id="edge25" class="edge">
482+<title>n0000009f:port1&#45;&gt;n00000061</title>
483+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1199.38,-211.11C1199.38,-211.11 1168.94,-214.22 1132.89,-217.91"/>
484+<polygon fill="black" stroke="black" points="1132.19,-214.46 1122.6,-218.96 1132.9,-221.42 1132.19,-214.46"/>
485+</g>
486+<!-- n0000009f&#45;&gt;n00000065 -->
487+<g id="edge26" class="edge">
488+<title>n0000009f:port2&#45;&gt;n00000065</title>
489+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1238.48,-200.11C1238.48,-200.11 1373.26,-188.53 1410.58,-196.91 1444.02,-204.43 1447.24,-220.06 1479.66,-231.18 1479.76,-231.22 1479.86,-231.25 1479.96,-231.29"/>
490+<polygon fill="black" stroke="black" points="1478.86,-234.61 1489.45,-234.34 1481,-227.95 1478.86,-234.61"/>
491+</g>
492+<!-- n0000009f&#45;&gt;n00000069 -->
493+<g id="edge27" class="edge">
494+<title>n0000009f:port3&#45;&gt;n00000069</title>
495+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1264.38,-200.11C1264.38,-200.11 1266.8,-198.46 1270.49,-195.94"/>
496+<polygon fill="black" stroke="black" points="1272.56,-198.77 1278.86,-190.24 1268.62,-192.98 1272.56,-198.77"/>
497+</g>
498+<!-- n0000009f&#45;&gt;n0000006d -->
499+<g id="edge28" class="edge">
500+<title>n0000009f:port4&#45;&gt;n0000006d</title>
501+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1290.47,-200.09C1290.47,-200.09 1203.71,-189.63 1196.18,-196.91 1167.29,-224.84 1172.63,-254.76 1196.18,-287.31 1198.16,-290.05 1207.33,-293.96 1219.38,-298.13"/>
502+<polygon fill="black" stroke="black" points="1218.49,-301.53 1229.09,-301.36 1220.7,-294.88 1218.49,-301.53"/>
503+</g>
504+<!-- n0000009f&#45;&gt;n00000071 -->
505+<g id="edge29" class="edge">
506+<title>n0000009f:port5&#45;&gt;n00000071</title>
507+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1316.28,-200.09C1316.28,-200.09 1402.13,-190.7 1410.58,-196.91 1486.41,-252.66 1489.83,-378.79 1487.09,-436.2"/>
508+<polygon fill="black" stroke="black" points="1483.59,-436.23 1486.51,-446.41 1490.58,-436.63 1483.59,-436.23"/>
509+</g>
510+<!-- n0000009f&#45;&gt;n00000075 -->
511+<g id="edge30" class="edge">
512+<title>n0000009f:port6&#45;&gt;n00000075</title>
513+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1342.28,-200.11C1342.28,-200.11 1202.85,-192.29 1196.18,-196.91 1121.26,-248.86 1108.3,-368.49 1106.45,-424.12"/>
514+<polygon fill="black" stroke="black" points="1102.94,-424.28 1106.21,-434.36 1109.94,-424.44 1102.94,-424.28"/>
515+</g>
516+<!-- n0000009f&#45;&gt;n00000079 -->
517+<g id="edge31" class="edge">
518+<title>n0000009f:port7&#45;&gt;n00000079</title>
519+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1368.38,-200.11C1368.38,-200.11 1201.84,-195.85 1197.94,-193.16 1149.87,-159.96 1132.85,-88.83 1126.94,-48.6"/>
520+<polygon fill="black" stroke="black" points="1130.39,-48.06 1125.6,-38.61 1123.46,-48.98 1130.39,-48.06"/>
521+</g>
522+<!-- n0000009f&#45;&gt;n0000007d -->
523+<g id="edge32" class="edge">
524+<title>n0000009f:port8&#45;&gt;n0000007d</title>
525+<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1394.38,-200.11C1394.38,-200.11 1412.45,-196.49 1416.34,-193.16 1457.25,-158.2 1479.47,-97.38 1489.62,-61.41"/>
526+<polygon fill="black" stroke="black" points="1493.06,-62.09 1492.28,-51.53 1486.3,-60.27 1493.06,-62.09"/>
527+</g>
528+<!-- n000000e9 -->
529+<g id="node37" class="node">
530+<title>n000000e9</title>
531+<path fill="green" stroke="black" d="M398.65,-431.45C398.65,-431.45 511.65,-431.45 511.65,-431.45 517.65,-431.45 523.65,-437.45 523.65,-443.45 523.65,-443.45 523.65,-503.45 523.65,-503.45 523.65,-509.45 517.65,-515.45 511.65,-515.45 511.65,-515.45 398.65,-515.45 398.65,-515.45 392.65,-515.45 386.65,-509.45 386.65,-503.45 386.65,-503.45 386.65,-443.45 386.65,-443.45 386.65,-437.45 392.65,-431.45 398.65,-431.45"/>
532+<text text-anchor="middle" x="420.65" y="-500.25" font-family="Times,serif" font-size="14.00">1</text>
533+<polyline fill="none" stroke="black" points="454.65,-492.45 454.65,-515.45 "/>
534+<text text-anchor="middle" x="489.15" y="-500.25" font-family="Times,serif" font-size="14.00">2</text>
535+<polyline fill="none" stroke="black" points="386.65,-492.45 523.65,-492.45 "/>
536+<text text-anchor="middle" x="455.15" y="-477.25" font-family="Times,serif" font-size="14.00">ov2740 4&#45;0036</text>
537+<text text-anchor="middle" x="455.15" y="-462.25" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev4</text>
538+<polyline fill="none" stroke="black" points="386.65,-454.45 523.65,-454.45 "/>
539+<text text-anchor="middle" x="455.15" y="-439.25" font-family="Times,serif" font-size="14.00">0</text>
540+</g>
541+<!-- n000000e9&#45;&gt;n0000008b -->
542+<g id="edge33" class="edge">
543+<title>n000000e9:port0&#45;&gt;n0000008b:port0</title>
544+<path fill="none" stroke="black" stroke-width="2" d="M386.14,-442.55C386.14,-442.55 361.11,-493.23 383.45,-518.65 391.47,-527.78 484.31,-519.72 492.3,-528.88 508.64,-547.6 499.26,-579.87 493.12,-595.68"/>
545+<polygon fill="black" stroke="black" stroke-width="2" points="489.86,-594.41 489.11,-604.98 496.29,-597.19 489.86,-594.41"/>
546+</g>
547+</g>
548+</svg>
Documentation/admin-guide/media/mgb4.rst+15−20
@@ -1,8 +1,10 @@
11 .. SPDX-License-Identifier: GPL-2.0
22
3−====================
4−mgb4 sysfs interface
5−====================
3+The mgb4 driver
4+===============
5+
6+sysfs interface
7+---------------
68
79 The mgb4 driver provides a sysfs interface, that is used to configure video
810 stream related parameters (some of them must be set properly before the v4l2
@@ -12,9 +14,8 @@ There are two types of parameters - global / PCI card related, found under · 3 unmodified lines
1214 ``/sys/class/video4linux/videoX/device`` and module specific found under
1315 ``/sys/class/video4linux/videoX``.
1416
15−
1617 Global (PCI card) parameters
17−============================
18+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1819
1920 **module_type** (R):
2021 Module type.
@@ -42,9 +43,8 @@ Global (PCI card) parameters · 21 unmodified lines
4243
4344 where each component is a 8b number.
4445
45−
4646 Common FPDL3/GMSL input parameters
47−==================================
47+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4848
4949 **input_id** (R):
5050 Input number ID, zero based.
@@ -190,9 +190,8 @@ Common FPDL3/GMSL input parameters · 139 unmodified lines
190190 *Note: This parameter can not be changed while the input v4l2 device is
191191 open.*
192192
193−
194193 Common FPDL3/GMSL output parameters
195−===================================
194+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
196195
197196 **output_id** (R):
198197 Output number ID, zero based.
@@ -282,9 +281,8 @@ Common FPDL3/GMSL output parameters · 83 unmodified lines
282281 Number of video lines between the end of the last valid pixel line (marked
283282 by DE=1) and assertion of the VSYNC signal. The default value is 2.
284283
285−
286284 FPDL3 specific input parameters
287−===============================
285+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288286
289287 **fpdl3_input_width** (RW):
290288 Number of deserializer input lines.
@@ -294,7 +292,7 @@ FPDL3 specific input parameters · 3 unmodified lines
294292 | 2 - dual
295293
296294 FPDL3 specific output parameters
297−================================
295+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
298296
299297 **fpdl3_output_width** (RW):
300298 Number of serializer output lines.
@@ -304,7 +302,7 @@ FPDL3 specific output parameters · 3 unmodified lines
304302 | 2 - dual
305303
306304 GMSL specific input parameters
307−==============================
305+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
308306
309307 **gmsl_mode** (RW):
310308 GMSL speed mode.
@@ -328,10 +326,8 @@ GMSL specific input parameters · 17 unmodified lines
328326 | 0 - disabled
329327 | 1 - enabled (default)
330328
331−
332−====================
333−mgb4 mtd partitions
334−====================
329+MTD partitions
330+--------------
335331
336332 The mgb4 driver creates a MTD device with two partitions:
337333 - mgb4-fw.X - FPGA firmware.
@@ -344,9 +340,8 @@ also have a third partition named *mgb4-flash* available in the system. This · 6 unmodified lines
344340 partition represents the whole, unpartitioned, card's FLASH memory and one should
345341 not fiddle with it...
346342
347−====================
348−mgb4 iio (triggers)
349−====================
343+IIO (triggers)
344+--------------
350345
351346 The mgb4 driver creates an Industrial I/O (IIO) device that provides trigger and
352347 signal level status capability. The following scan elements are available:
Documentation/admin-guide/media/v4l-drivers.rst+1−0
@@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation · 15 unmodified lines
1616 imx
1717 imx7
1818 ipu3
19+ ipu6-isys
1920 ivtv
2021 mgb4
2122 omap3isp
Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml+127−0
@@ -0,0 +1,127 @@
1+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2+%YAML 1.2
3+---
4+$id: http://devicetree.org/schemas/media/brcm,bcm2835-unicam.yaml#
5+$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+title: Broadcom BCM283x Camera Interface (Unicam)
8+
9+maintainers:
10+ - Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
11+
12+description: |-
13+ The Unicam block on BCM283x SoCs is the receiver for either
14+ CSI-2 or CCP2 data from image sensors or similar devices.
15+
16+ The main platform using this SoC is the Raspberry Pi family of boards. On
17+ the Pi the VideoCore firmware can also control this hardware block, and
18+ driving it from two different processors will cause issues. To avoid this,
19+ the firmware checks the device tree configuration during boot. If it finds
20+ device tree nodes whose name starts with 'csi' then it will stop the firmware
21+ accessing the block, and it can then safely be used via the device tree
22+ binding.
23+
24+properties:
25+ compatible:
26+ const: brcm,bcm2835-unicam
27+
28+ reg:
29+ items:
30+ - description: Unicam block.
31+ - description: Clock Manager Image (CMI) block.
32+
33+ reg-names:
34+ items:
35+ - const: unicam
36+ - const: cmi
37+
38+ interrupts:
39+ maxItems: 1
40+
41+ clocks:
42+ items:
43+ - description: Clock to drive the LP state machine of Unicam.
44+ - description: Clock for the VPU (core clock).
45+
46+ clock-names:
47+ items:
48+ - const: lp
49+ - const: vpu
50+
51+ power-domains:
52+ items:
53+ - description: Unicam power domain
54+
55+ brcm,num-data-lanes:
56+ $ref: /schemas/types.yaml#/definitions/uint32
57+ enum: [ 2, 4 ]
58+ description: |
59+ Number of CSI-2 data lanes supported by this Unicam instance. The number
60+ of data lanes actively used is specified with the data-lanes endpoint
61+ property.
62+
63+ port:
64+ $ref: /schemas/graph.yaml#/$defs/port-base
65+ unevaluatedProperties: false
66+
67+ properties:
68+ endpoint:
69+ $ref: /schemas/media/video-interfaces.yaml#
70+ additionalProperties: false
71+
72+ properties:
73+ bus-type:
74+ enum: [ 3, 4 ]
75+
76+ clock-noncontinuous: true
77+ data-lanes: true
78+ remote-endpoint: true
79+
80+ required:
81+ - bus-type
82+ - data-lanes
83+ - remote-endpoint
84+
85+ required:
86+ - endpoint
87+
88+required:
89+ - compatible
90+ - reg
91+ - reg-names
92+ - interrupts
93+ - clocks
94+ - clock-names
95+ - power-domains
96+ - brcm,num-data-lanes
97+ - port
98+
99+additionalProperties: False
100+
101+examples:
102+ - |
103+ #include <dt-bindings/clock/bcm2835.h>
104+ #include <dt-bindings/interrupt-controller/arm-gic.h>
105+ #include <dt-bindings/media/video-interfaces.h>
106+ #include <dt-bindings/power/raspberrypi-power.h>
107+
108+ csi1: csi@7e801000 {
109+ compatible = "brcm,bcm2835-unicam";
110+ reg = <0x7e801000 0x800>,
111+ <0x7e802004 0x4>;
112+ reg-names = "unicam", "cmi";
113+ interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
114+ clocks = <&clocks BCM2835_CLOCK_CAM1>,
115+ <&firmware_clocks 4>;
116+ clock-names = "lp", "vpu";
117+ power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>;
118+ brcm,num-data-lanes = <2>;
119+ port {
120+ csi1_ep: endpoint {
121+ remote-endpoint = <&imx219_0>;
122+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
123+ data-lanes = <1 2>;
124+ };
125+ };
126+ };
127+...
Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml+1−1
@@ -15,7 +15,7 @@ description: | · 14 unmodified lines
1515 They include an ISP capable of auto exposure and auto white balance.
1616
1717 allOf:
18− - $ref: ../video-interface-devices.yaml#
18+ - $ref: /schemas/media/video-interface-devices.yaml#
1919
2020 properties:
2121 compatible:
Documentation/devicetree/bindings/media/i2c/galaxycore,gc2145.yaml+1−1
@@ -19,7 +19,7 @@ description: · 18 unmodified lines
1919 either through a parallel interface or through MIPI CSI-2.
2020
2121 allOf:
22− - $ref: ../video-interface-devices.yaml#
22+ - $ref: /schemas/media/video-interface-devices.yaml#
2323
2424 properties:
2525 compatible:
Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml+25−10
@@ -37,31 +37,45 @@ properties: · 36 unmodified lines
3737 active low.
3838 maxItems: 1
3939
40− dovdd-supply:
40+ DOVDD-supply:
4141 description:
4242 Definition of the regulator used as interface power supply.
4343
44− avdd-supply:
44+ AVDD-supply:
4545 description:
4646 Definition of the regulator used as analog power supply.
4747
48− dvdd-supply:
48+ DVDD-supply:
4949 description:
5050 Definition of the regulator used as digital power supply.
5151
5252 port:
53− $ref: /schemas/graph.yaml#/properties/port
5453 description:
5554 A node containing an output port node.
55+ $ref: /schemas/graph.yaml#/$defs/port-base
56+ additionalProperties: false
57+
58+ properties:
59+ endpoint:
60+ $ref: /schemas/media/video-interfaces.yaml#
61+ additionalProperties: false
62+
63+ properties:
64+ link-frequencies: true
65+
66+ remote-endpoint: true
67+
68+ required:
69+ - link-frequencies
5670
5771 required:
5872 - compatible
5973 - reg
6074 - clocks
6175 - clock-names
62− - dovdd-supply
63− - avdd-supply
64− - dvdd-supply
76+ - DOVDD-supply
77+ - AVDD-supply
78+ - DVDD-supply
6579 - reset-gpios
6680 - port
6781
@@ -82,13 +96,14 @@ examples: · 14 unmodified lines
8296 clock-names = "xvclk";
8397 reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
8498
85− dovdd-supply = <&sw2_reg>;
86− dvdd-supply = <&sw2_reg>;
87− avdd-supply = <&reg_peri_3p15v>;
99+ DOVDD-supply = <&sw2_reg>;
100+ DVDD-supply = <&sw2_reg>;
101+ AVDD-supply = <&reg_peri_3p15v>;
88102
89103 port {
90104 ov2680_to_mipi: endpoint {
91105 remote-endpoint = <&mipi_from_sensor>;
106+ link-frequencies = /bits/ 64 <330000000>;
92107 };
93108 };
94109 };
Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yamlrenamed from Documentation/devicetree/bindings/media/i2c/ov8856.yaml+1−1
@@ -2,7 +2,7 @@ · 1 unmodified line
22 # Copyright (c) 2019 MediaTek Inc.
33 %YAML 1.2
44 ---
5−$id: http://devicetree.org/schemas/media/i2c/ov8856.yaml#
5+$id: http://devicetree.org/schemas/media/i2c/ovti,ov8856.yaml#
66 $schema: http://devicetree.org/meta-schemas/core.yaml#
77
88 title: Omnivision OV8856 CMOS Sensor
Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml+1−1
@@ -16,7 +16,7 @@ description: | · 15 unmodified lines
1616 maximum throughput of 1.2Gbps/lane.
1717
1818 allOf:
19− - $ref: ../video-interface-devices.yaml#
19+ - $ref: /schemas/media/video-interface-devices.yaml#
2020
2121 properties:
2222 compatible:
Documentation/devicetree/bindings/media/i2c/sony,imx290.yaml+4−1
@@ -23,6 +23,9 @@ description: |- · 22 unmodified lines
2323 is treated the same as this as it was the original compatible string.
2424 imx290llr is the mono version of the sensor.
2525
26+allOf:
27+ - $ref: /schemas/media/video-interface-devices.yaml#
28+
2629 properties:
2730 compatible:
2831 oneOf:
@@ -101,7 +104,7 @@ required: · 72 unmodified lines
101104 - vdddo-supply
102105 - port
103106
104−additionalProperties: false
107+unevaluatedProperties: false
105108
106109 examples:
107110 - |
Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml+1−1
@@ -18,7 +18,7 @@ description: |- · 17 unmodified lines
1818 available via CSI-2 serial data output (two or four lanes).
1919
2020 allOf:
21− - $ref: ../video-interface-devices.yaml#
21+ - $ref: /schemas/media/video-interface-devices.yaml#
2222
2323 properties:
2424 compatible:
Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml+1−0
@@ -84,6 +84,7 @@ allOf: · 83 unmodified lines
8484 properties:
8585 port@0:
8686 description: MIPI CSI-2 RX
87+ port@1: false
8788 required:
8889 - port@0
8990
Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml+11−0
@@ -31,6 +31,11 @@ properties: · 30 unmodified lines
3131 reg:
3232 maxItems: 1
3333
34+ clocks:
35+ items:
36+ - description: AXI DMA engine clock for fetching JPEG bitstream from memory (per)
37+ - description: IP bus clock for register access (ipg)
38+
3439 interrupts:
3540 description: |
3641 There are 4 slots available in the IP, which the driver may use
@@ -49,6 +54,7 @@ properties: · 12 unmodified lines
4954 required:
5055 - compatible
5156 - reg
57+ - clocks
5258 - interrupts
5359 - power-domains
5460
@@ -56,12 +62,15 @@ additionalProperties: false · 1 unmodified line
5662
5763 examples:
5864 - |
65+ #include <dt-bindings/clock/imx8-lpcg.h>
5966 #include <dt-bindings/interrupt-controller/arm-gic.h>
6067 #include <dt-bindings/firmware/imx/rsrc.h>
6168
6269 jpegdec: jpegdec@58400000 {
6370 compatible = "nxp,imx8qxp-jpgdec";
6471 reg = <0x58400000 0x00050000 >;
72+ clocks = <&img_jpeg_dec_lpcg IMX_LPCG_CLK_0>,
73+ <&img_jpeg_dec_lpcg IMX_LPCG_CLK_4>;
6574 interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>,
6675 <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
6776 <GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>,
@@ -76,6 +85,8 @@ examples: · 8 unmodified lines
7685 jpegenc: jpegenc@58450000 {
7786 compatible = "nxp,imx8qm-jpgenc", "nxp,imx8qxp-jpgenc";
7887 reg = <0x58450000 0x00050000 >;
88+ clocks = <&img_jpeg_enc_lpcg IMX_LPCG_CLK_0>,
89+ <&img_jpeg__lpcg IMX_LPCG_CLK_4>;
7990 interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>,
8091 <GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>,
8192 <GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>,
Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml+512−0
@@ -0,0 +1,512 @@
1+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
2+%YAML 1.2
3+---
4+$id: http://devicetree.org/schemas/media/qcom,sc8280xp-camss.yaml#
5+$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+title: Qualcomm SC8280XP Camera Subsystem (CAMSS)
8+
9+maintainers:
10+ - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
11+
12+description: |
13+ The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms.
14+
15+properties:
16+ compatible:
17+ const: qcom,sc8280xp-camss
18+
19+ clocks:
20+ maxItems: 40
21+
22+ clock-names:
23+ items:
24+ - const: camnoc_axi
25+ - const: cpas_ahb
26+ - const: csiphy0
27+ - const: csiphy0_timer
28+ - const: csiphy1
29+ - const: csiphy1_timer
30+ - const: csiphy2
31+ - const: csiphy2_timer
32+ - const: csiphy3
33+ - const: csiphy3_timer
34+ - const: vfe0_axi
35+ - const: vfe0
36+ - const: vfe0_cphy_rx
37+ - const: vfe0_csid
38+ - const: vfe1_axi
39+ - const: vfe1
40+ - const: vfe1_cphy_rx
41+ - const: vfe1_csid
42+ - const: vfe2_axi
43+ - const: vfe2
44+ - const: vfe2_cphy_rx
45+ - const: vfe2_csid
46+ - const: vfe3_axi
47+ - const: vfe3
48+ - const: vfe3_cphy_rx
49+ - const: vfe3_csid
50+ - const: vfe_lite0
51+ - const: vfe_lite0_cphy_rx
52+ - const: vfe_lite0_csid
53+ - const: vfe_lite1
54+ - const: vfe_lite1_cphy_rx
55+ - const: vfe_lite1_csid
56+ - const: vfe_lite2
57+ - const: vfe_lite2_cphy_rx
58+ - const: vfe_lite2_csid
59+ - const: vfe_lite3
60+ - const: vfe_lite3_cphy_rx
61+ - const: vfe_lite3_csid
62+ - const: gcc_axi_hf
63+ - const: gcc_axi_sf
64+
65+ interrupts:
66+ maxItems: 20
67+
68+ interrupt-names:
69+ items:
70+ - const: csid1_lite
71+ - const: vfe_lite1
72+ - const: csiphy3
73+ - const: csid0
74+ - const: vfe0
75+ - const: csid1
76+ - const: vfe1
77+ - const: csid0_lite
78+ - const: vfe_lite0
79+ - const: csiphy0
80+ - const: csiphy1
81+ - const: csiphy2
82+ - const: csid2
83+ - const: vfe2
84+ - const: csid3_lite
85+ - const: csid2_lite
86+ - const: vfe_lite3
87+ - const: vfe_lite2
88+ - const: csid3
89+ - const: vfe3
90+
91+ iommus:
92+ maxItems: 16
93+
94+ interconnects:
95+ maxItems: 4
96+
97+ interconnect-names:
98+ items:
99+ - const: cam_ahb
100+ - const: cam_hf_mnoc
101+ - const: cam_sf_mnoc
102+ - const: cam_sf_icp_mnoc
103+
104+ power-domains:
105+ items:
106+ - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller.
107+ - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
108+ - description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller.
109+ - description: IFE3 GDSC - Image Front End, Global Distributed Switch Controller.
110+ - description: Titan Top GDSC - Titan ISP Block, Global Distributed Switch Controller.
111+
112+ power-domain-names:
113+ items:
114+ - const: ife0
115+ - const: ife1
116+ - const: ife2
117+ - const: ife3
118+ - const: top
119+
120+ ports:
121+ $ref: /schemas/graph.yaml#/properties/ports
122+
123+ description:
124+ CSI input ports.
125+
126+ properties:
127+ port@0:
128+ $ref: /schemas/graph.yaml#/$defs/port-base
129+ unevaluatedProperties: false
130+ description:
131+ Input port for receiving CSI data from CSIPHY0.
132+
133+ properties:
134+ endpoint:
135+ $ref: video-interfaces.yaml#
136+ unevaluatedProperties: false
137+
138+ properties:
139+ clock-lanes:
140+ maxItems: 1
141+
142+ data-lanes:
143+ minItems: 1
144+ maxItems: 4
145+
146+ required:
147+ - clock-lanes
148+ - data-lanes
149+
150+ port@1:
151+ $ref: /schemas/graph.yaml#/$defs/port-base
152+ unevaluatedProperties: false
153+ description:
154+ Input port for receiving CSI data from CSIPHY1.
155+
156+ properties:
157+ endpoint:
158+ $ref: video-interfaces.yaml#
159+ unevaluatedProperties: false
160+
161+ properties:
162+ clock-lanes:
163+ maxItems: 1
164+
165+ data-lanes:
166+ minItems: 1
167+ maxItems: 4
168+
169+ required:
170+ - clock-lanes
171+ - data-lanes
172+
173+ port@2:
174+ $ref: /schemas/graph.yaml#/$defs/port-base
175+ unevaluatedProperties: false
176+ description:
177+ Input port for receiving CSI data from CSIPHY2.
178+
179+ properties:
180+ endpoint:
181+ $ref: video-interfaces.yaml#
182+ unevaluatedProperties: false
183+
184+ properties:
185+ clock-lanes:
186+ maxItems: 1
187+
188+ data-lanes:
189+ minItems: 1
190+ maxItems: 4
191+
192+ required:
193+ - clock-lanes
194+ - data-lanes
195+
196+ port@3:
197+ $ref: /schemas/graph.yaml#/$defs/port-base
198+ unevaluatedProperties: false
199+ description:
200+ Input port for receiving CSI data from CSIPHY3.
201+
202+ properties:
203+ endpoint:
204+ $ref: video-interfaces.yaml#
205+ unevaluatedProperties: false
206+
207+ properties:
208+ clock-lanes:
209+ maxItems: 1
210+
211+ data-lanes:
212+ minItems: 1
213+ maxItems: 4
214+
215+ required:
216+ - clock-lanes
217+ - data-lanes
218+
219+ reg:
220+ maxItems: 20
221+
222+ reg-names:
223+ items:
224+ - const: csiphy2
225+ - const: csiphy3
226+ - const: csiphy0
227+ - const: csiphy1
228+ - const: vfe0
229+ - const: csid0
230+ - const: vfe1
231+ - const: csid1
232+ - const: vfe2
233+ - const: csid2
234+ - const: vfe_lite0
235+ - const: csid0_lite
236+ - const: vfe_lite1
237+ - const: csid1_lite
238+ - const: vfe_lite2
239+ - const: csid2_lite
240+ - const: vfe_lite3
241+ - const: csid3_lite
242+ - const: vfe3
243+ - const: csid3
244+
245+ vdda-phy-supply:
246+ description:
247+ Phandle to a regulator supply to PHY core block.
248+
249+ vdda-pll-supply:
250+ description:
251+ Phandle to 1.8V regulator supply to PHY refclk pll block.
252+
253+required:
254+ - clock-names
255+ - clocks
256+ - compatible
257+ - interconnects
258+ - interconnect-names
259+ - interrupts
260+ - interrupt-names
261+ - iommus
262+ - power-domains
263+ - power-domain-names
264+ - reg
265+ - reg-names
266+ - vdda-phy-supply
267+ - vdda-pll-supply
268+
269+additionalProperties: false
270+
271+examples:
272+ - |
273+ #include <dt-bindings/interrupt-controller/arm-gic.h>
274+ #include <dt-bindings/clock/qcom,gcc-sc8280xp.h>
275+ #include <dt-bindings/clock/qcom,sc8280xp-camcc.h>
276+ #include <dt-bindings/interconnect/qcom,sc8280xp.h>
277+ #include <dt-bindings/power/qcom-rpmpd.h>
278+
279+ soc {
280+ #address-cells = <2>;
281+ #size-cells = <2>;
282+
283+ camss: camss@ac5a000 {
284+ compatible = "qcom,sc8280xp-camss";
285+
286+ reg = <0 0x0ac5a000 0 0x2000>,
287+ <0 0x0ac5c000 0 0x2000>,
288+ <0 0x0ac65000 0 0x2000>,
289+ <0 0x0ac67000 0 0x2000>,
290+ <0 0x0acaf000 0 0x4000>,
291+ <0 0x0acb3000 0 0x1000>,
292+ <0 0x0acb6000 0 0x4000>,
293+ <0 0x0acba000 0 0x1000>,
294+ <0 0x0acbd000 0 0x4000>,
295+ <0 0x0acc1000 0 0x1000>,
296+ <0 0x0acc4000 0 0x4000>,
297+ <0 0x0acc8000 0 0x1000>,
298+ <0 0x0accb000 0 0x4000>,
299+ <0 0x0accf000 0 0x1000>,
300+ <0 0x0acd2000 0 0x4000>,
301+ <0 0x0acd6000 0 0x1000>,
302+ <0 0x0acd9000 0 0x4000>,
303+ <0 0x0acdd000 0 0x1000>,
304+ <0 0x0ace0000 0 0x4000>,
305+ <0 0x0ace4000 0 0x1000>;
306+
307+ reg-names = "csiphy2",
308+ "csiphy3",
309+ "csiphy0",
310+ "csiphy1",
311+ "vfe0",
312+ "csid0",
313+ "vfe1",
314+ "csid1",
315+ "vfe2",
316+ "csid2",
317+ "vfe_lite0",
318+ "csid0_lite",
319+ "vfe_lite1",
320+ "csid1_lite",
321+ "vfe_lite2",
322+ "csid2_lite",
323+ "vfe_lite3",
324+ "csid3_lite",
325+ "vfe3",
326+ "csid3";
327+
328+ vdda-phy-supply = <&vreg_l6d>;
329+ vdda-pll-supply = <&vreg_l4d>;
330+
331+ interrupts = <GIC_SPI 359 IRQ_TYPE_LEVEL_HIGH>,
332+ <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH>,
333+ <GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>,
334+ <GIC_SPI 464 IRQ_TYPE_LEVEL_HIGH>,
335+ <GIC_SPI 465 IRQ_TYPE_LEVEL_HIGH>,
336+ <GIC_SPI 466 IRQ_TYPE_LEVEL_HIGH>,
337+ <GIC_SPI 467 IRQ_TYPE_LEVEL_HIGH>,
338+ <GIC_SPI 468 IRQ_TYPE_LEVEL_HIGH>,
339+ <GIC_SPI 469 IRQ_TYPE_LEVEL_HIGH>,
340+ <GIC_SPI 477 IRQ_TYPE_LEVEL_HIGH>,
341+ <GIC_SPI 478 IRQ_TYPE_LEVEL_HIGH>,
342+ <GIC_SPI 479 IRQ_TYPE_LEVEL_HIGH>,
343+ <GIC_SPI 640 IRQ_TYPE_LEVEL_HIGH>,
344+ <GIC_SPI 641 IRQ_TYPE_LEVEL_HIGH>,
345+ <GIC_SPI 758 IRQ_TYPE_LEVEL_HIGH>,
346+ <GIC_SPI 759 IRQ_TYPE_LEVEL_HIGH>,
347+ <GIC_SPI 760 IRQ_TYPE_LEVEL_HIGH>,
348+ <GIC_SPI 761 IRQ_TYPE_LEVEL_HIGH>,
349+ <GIC_SPI 762 IRQ_TYPE_LEVEL_HIGH>,
350+ <GIC_SPI 764 IRQ_TYPE_LEVEL_HIGH>;
351+
352+ interrupt-names = "csid1_lite",
353+ "vfe_lite1",
354+ "csiphy3",
355+ "csid0",
356+ "vfe0",
357+ "csid1",
358+ "vfe1",
359+ "csid0_lite",
360+ "vfe_lite0",
361+ "csiphy0",
362+ "csiphy1",
363+ "csiphy2",
364+ "csid2",
365+ "vfe2",
366+ "csid3_lite",
367+ "csid2_lite",
368+ "vfe_lite3",
369+ "vfe_lite2",
370+ "csid3",
371+ "vfe3";
372+
373+ power-domains = <&camcc IFE_0_GDSC>,
374+ <&camcc IFE_1_GDSC>,
375+ <&camcc IFE_2_GDSC>,
376+ <&camcc IFE_3_GDSC>,
377+ <&camcc TITAN_TOP_GDSC>;
378+
379+ power-domain-names = "ife0",
380+ "ife1",
381+ "ife2",
382+ "ife3",
383+ "top";
384+
385+ clocks = <&camcc CAMCC_CAMNOC_AXI_CLK>,
386+ <&camcc CAMCC_CPAS_AHB_CLK>,
387+ <&camcc CAMCC_CSIPHY0_CLK>,
388+ <&camcc CAMCC_CSI0PHYTIMER_CLK>,
389+ <&camcc CAMCC_CSIPHY1_CLK>,
390+ <&camcc CAMCC_CSI1PHYTIMER_CLK>,
391+ <&camcc CAMCC_CSIPHY2_CLK>,
392+ <&camcc CAMCC_CSI2PHYTIMER_CLK>,
393+ <&camcc CAMCC_CSIPHY3_CLK>,
394+ <&camcc CAMCC_CSI3PHYTIMER_CLK>,
395+ <&camcc CAMCC_IFE_0_AXI_CLK>,
396+ <&camcc CAMCC_IFE_0_CLK>,
397+ <&camcc CAMCC_IFE_0_CPHY_RX_CLK>,
398+ <&camcc CAMCC_IFE_0_CSID_CLK>,
399+ <&camcc CAMCC_IFE_1_AXI_CLK>,
400+ <&camcc CAMCC_IFE_1_CLK>,
401+ <&camcc CAMCC_IFE_1_CPHY_RX_CLK>,
402+ <&camcc CAMCC_IFE_1_CSID_CLK>,
403+ <&camcc CAMCC_IFE_2_AXI_CLK>,
404+ <&camcc CAMCC_IFE_2_CLK>,
405+ <&camcc CAMCC_IFE_2_CPHY_RX_CLK>,
406+ <&camcc CAMCC_IFE_2_CSID_CLK>,
407+ <&camcc CAMCC_IFE_3_AXI_CLK>,
408+ <&camcc CAMCC_IFE_3_CLK>,
409+ <&camcc CAMCC_IFE_3_CPHY_RX_CLK>,
410+ <&camcc CAMCC_IFE_3_CSID_CLK>,
411+ <&camcc CAMCC_IFE_LITE_0_CLK>,
412+ <&camcc CAMCC_IFE_LITE_0_CPHY_RX_CLK>,
413+ <&camcc CAMCC_IFE_LITE_0_CSID_CLK>,
414+ <&camcc CAMCC_IFE_LITE_1_CLK>,
415+ <&camcc CAMCC_IFE_LITE_1_CPHY_RX_CLK>,
416+ <&camcc CAMCC_IFE_LITE_1_CSID_CLK>,
417+ <&camcc CAMCC_IFE_LITE_2_CLK>,
418+ <&camcc CAMCC_IFE_LITE_2_CPHY_RX_CLK>,
419+ <&camcc CAMCC_IFE_LITE_2_CSID_CLK>,
420+ <&camcc CAMCC_IFE_LITE_3_CLK>,
421+ <&camcc CAMCC_IFE_LITE_3_CPHY_RX_CLK>,
422+ <&camcc CAMCC_IFE_LITE_3_CSID_CLK>,
423+ <&gcc GCC_CAMERA_HF_AXI_CLK>,
424+ <&gcc GCC_CAMERA_SF_AXI_CLK>;
425+
426+ clock-names = "camnoc_axi",
427+ "cpas_ahb",
428+ "csiphy0",
429+ "csiphy0_timer",
430+ "csiphy1",
431+ "csiphy1_timer",
432+ "csiphy2",
433+ "csiphy2_timer",
434+ "csiphy3",
435+ "csiphy3_timer",
436+ "vfe0_axi",
437+ "vfe0",
438+ "vfe0_cphy_rx",
439+ "vfe0_csid",
440+ "vfe1_axi",
441+ "vfe1",
442+ "vfe1_cphy_rx",
443+ "vfe1_csid",
444+ "vfe2_axi",
445+ "vfe2",
446+ "vfe2_cphy_rx",
447+ "vfe2_csid",
448+ "vfe3_axi",
449+ "vfe3",
450+ "vfe3_cphy_rx",
451+ "vfe3_csid",
452+ "vfe_lite0",
453+ "vfe_lite0_cphy_rx",
454+ "vfe_lite0_csid",
455+ "vfe_lite1",
456+ "vfe_lite1_cphy_rx",
457+ "vfe_lite1_csid",
458+ "vfe_lite2",
459+ "vfe_lite2_cphy_rx",
460+ "vfe_lite2_csid",
461+ "vfe_lite3",
462+ "vfe_lite3_cphy_rx",
463+ "vfe_lite3_csid",
464+ "gcc_axi_hf",
465+ "gcc_axi_sf";
466+
467+
468+ iommus = <&apps_smmu 0x2000 0x4e0>,
469+ <&apps_smmu 0x2020 0x4e0>,
470+ <&apps_smmu 0x2040 0x4e0>,
471+ <&apps_smmu 0x2060 0x4e0>,
472+ <&apps_smmu 0x2080 0x4e0>,
473+ <&apps_smmu 0x20e0 0x4e0>,
474+ <&apps_smmu 0x20c0 0x4e0>,
475+ <&apps_smmu 0x20a0 0x4e0>,
476+ <&apps_smmu 0x2400 0x4e0>,
477+ <&apps_smmu 0x2420 0x4e0>,
478+ <&apps_smmu 0x2440 0x4e0>,
479+ <&apps_smmu 0x2460 0x4e0>,
480+ <&apps_smmu 0x2480 0x4e0>,
481+ <&apps_smmu 0x24e0 0x4e0>,
482+ <&apps_smmu 0x24c0 0x4e0>,
483+ <&apps_smmu 0x24a0 0x4e0>;
484+
485+ interconnects = <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_CAMERA_CFG 0>,
486+ <&mmss_noc MASTER_CAMNOC_HF 0 &mc_virt SLAVE_EBI1 0>,
487+ <&mmss_noc MASTER_CAMNOC_SF 0 &mc_virt SLAVE_EBI1 0>,
488+ <&mmss_noc MASTER_CAMNOC_ICP 0 &mc_virt SLAVE_EBI1 0>;
489+ interconnect-names = "cam_ahb",
490+ "cam_hf_mnoc",
491+ "cam_sf_mnoc",
492+ "cam_sf_icp_mnoc";
493+
494+ ports {
495+ #address-cells = <1>;
496+ #size-cells = <0>;
497+
498+ port@0 {
499+ reg = <0>;
500+ #address-cells = <1>;
501+ #size-cells = <0>;
502+
503+ csiphy_ep0: endpoint@0 {
504+ reg = <0>;
505+ clock-lanes = <7>;
506+ data-lanes = <0 1>;
507+ remote-endpoint = <&sensor_ep>;
508+ };
509+ };
510+ };
511+ };
512+ };
Documentation/driver-api/media/drivers/index.rst+1−0
@@ -26,6 +26,7 @@ Video4Linux (V4L) drivers · 25 unmodified lines
2626 vimc-devel
2727 zoran
2828 ccs/ccs
29+ ipu6
2930
3031
3132 Digital TV drivers
Documentation/driver-api/media/drivers/ipu6.rst+205−0
@@ -0,0 +1,205 @@
1+.. SPDX-License-Identifier: GPL-2.0
2+
3+==================
4+Intel IPU6 Driver
5+==================
6+
7+Author: Bingbu Cao <bingbu.cao@intel.com>
8+
9+Overview
10+=========
11+
12+Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some
13+Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and
14+Meteor Lake. IPU6 consists of two major systems: Input System (ISYS) and
15+Processing System (PSYS). IPU6 are visible on the PCI bus as a single device, it
16+can be found by ``lspci``:
17+
18+``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)``
19+
20+IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is
21+visible for driver.
22+
23+Buttress
24+=========
25+
26+The IPU6 is connecting to the system fabric with Buttress which is enabling host
27+driver to control the IPU6, it also allows IPU6 access the system memory to
28+store and load frame pixel streams and any other metadata.
29+
30+Buttress mainly manages several system functionalities: power management,
31+interrupt handling, firmware authentication and global timer sync.
32+
33+ISYS and PSYS Power flow
34+------------------------
35+
36+IPU6 driver initialize the ISYS and PSYS power up or down request by setting the
37+Buttress frequency control register for ISYS and PSYS
38+(``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL``) in
39+function:
40+
41+.. c:function:: int ipu6_buttress_power(...)
42+
43+Buttress forwards the request to Punit, after Punit execute the power up flow,
44+Buttress indicates driver that ISYS or PSYS is powered up by updating the power
45+status registers.
46+
47+.. Note:: ISYS power up needs take place prior to PSYS power up, ISYS power down
48+ needs take place after PSYS power down due to hardware limitation.
49+
50+Interrupt
51+---------
52+
53+IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered when
54+ISYS, PSYS, Buttress event or error happen, driver can get the interrupt cause
55+by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``, driver
56+clears the irq status and then calls specific ISYS or PSYS irq handler.
57+
58+.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...)
59+
60+Security and firmware authentication
61+-------------------------------------
62+
63+To address the IPU6 firmware security concerns, the IPU6 firmware needs to
64+undergo an authentication process before it is allowed to executed on the IPU6
65+internal processors. The IPU6 driver will work with Converged Security Engine
66+(CSE) to complete authentication process. The CSE is responsible of
67+authenticating the IPU6 firmware. The authenticated firmware binary is copied
68+into an isolated memory region. Firmware authentication process is implemented
69+by CSE following an IPC handshake with the IPU6 driver. There are some Buttress
70+registers used by the CSE and the IPU6 driver to communicate with each other via
71+IPC.
72+
73+.. c:function:: int ipu6_buttress_authenticate(...)
74+
75+Global timer sync
76+-----------------
77+
78+The IPU6 driver initiates a Hammock Harbor synchronization flow each time it
79+starts camera operation. The IPU6 will synchronizes an internal counter in the
80+Buttress with a copy of the SoC time, this counter maintains the up-to-date time
81+until camera operation is stopped. The IPU6 driver can use this time counter to
82+calibrate the timestamp based on the timestamp in response event from firmware.
83+
84+.. c:function:: int ipu6_buttress_start_tsc_sync(...)
85+
86+DMA and MMU
87+============
88+
89+The IPU6 has its own scalar processor where the firmware run at and an internal
90+32-bit virtual address space. The IPU6 has MMU address translation hardware to
91+allow that scalar processors to access the internal memory and external system
92+memory through IPU6 virtual address. The address translation is based on two
93+levels of page lookup tables stored in system memory which are maintained by the
94+IPU6 driver. The IPU6 driver sets the level-1 page table base address to MMU
95+register and allows MMU to perform page table lookups.
96+
97+The IPU6 driver exports its own DMA operations. The IPU6 driver will update the
98+page table entries for each DMA operation and invalidate the MMU TLB after each
99+unmap and free.
100+
101+.. code-block:: none
102+
103+ const struct dma_map_ops ipu6_dma_ops = {
104+ .alloc = ipu6_dma_alloc,
105+ .free = ipu6_dma_free,
106+ .mmap = ipu6_dma_mmap,
107+ .map_sg = ipu6_dma_map_sg,
108+ .unmap_sg = ipu6_dma_unmap_sg,
109+ ...
110+ };
111+
112+.. Note:: IPU6 MMU works behind IOMMU so for each IPU6 DMA ops, driver will call
113+ generic PCI DMA ops to ask IOMMU to do the additional mapping if VT-d
114+ enabled.
115+
116+Firmware file format
117+====================
118+
119+The IPU6 firmware is in Code Partition Directory (CPD) file format. The CPD
120+firmware contains a CPD header, several CPD entries and components. The CPD
121+component includes 3 entries - manifest, metadata and module data. Manifest and
122+metadata are defined by CSE and used by CSE for authentication. Module data is
123+specific to IPU6 which holds the binary data of firmware called package
124+directory. The IPU6 driver (``ipu6-cpd.c`` in particular) parses and validates
125+the CPD firmware file and gets the package directory binary data of the IPU6
126+firmware, copies it to specific DMA buffer and sets its base address to Buttress
127+``FW_SOURCE_BASE`` register. Finally the CSE will do authentication for this
128+firmware binary.
129+
130+
131+Syscom interface
132+================
133+
134+The IPU6 driver communicates with firmware via the Syscom ABI. Syscom is an
135+inter-processor communication mechanism between the IPU scalar processors and
136+the CPU. There are a number of resources shared between firmware and software.
137+A system memory region where the message queues reside, firmware can access the
138+memory region via the IPU MMU. The Syscom queues are FIFO fixed depth queues
139+with a configurable number of tokens (messages). There are also common IPU6 MMIO
140+registers where the queue read and write indices reside. Software and firmware
141+function as producer and consumer of tokens in the queues and update the write
142+and read indices separately when sending or receiving each message.
143+
144+The IPU6 driver must prepare and configure the number of input and output
145+queues, configure the count of tokens per queue and the size of per token before
146+initiating and starting the communication with firmware. Firmware and software
147+must use same configurations. The IPU6 Buttress has a number of firmware boot
148+parameter registers which can be used to store the address of configuration and
149+initialise the Syscom state, then driver can request firmware to start and run via
150+setting the scalar processor control status register.
151+
152+Input System
153+============
154+
155+IPU6 input system consists of MIPI D-PHY and several CSI-2 receivers. It can
156+capture image pixel data from camera sensors or other MIPI CSI-2 output devices.
157+
158+D-PHYs and CSI-2 ports lane mapping
159+-----------------------------------
160+
161+The IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and
162+Alder Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL
163+D-PHY and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an
164+adaptional layer between D-PHY and CSI-2 receiver controller which includes port
165+configuration, PHY wrapper or private test interfaces for D-PHY. There are 3
166+D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and
167+``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6.
168+
169+Different IPU6 versions have different D-PHY lanes mappings, On Tiger Lake,
170+there are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports,
171+see the PPI mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper
172+Lake and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, the IPU6 supports
173+maximum 4 CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock
174+lanes so IPU6 support maximum 6 CSI-2 ports.
175+
176+.. Note:: Each pair of CSI-2 two ports is a single unit that can share the data
177+ lanes. For example, for CSI-2 port 0 and 1, CSI-2 port 0 support
178+ maximum 4 data lanes, CSI-2 port 1 support maximum 2 data lanes, CSI-2
179+ port 0 with 2 data lanes can work together with CSI-2 port 1 with 2
180+ data lanes. If trying to use CSI-2 port 0 with 4 lanes, CSI-2 port 1
181+ will not be available as the 4 data lanes are shared by CSI-2 port 0
182+ and 1. The same applies to CSI ports 2/3, 4/5 and 7/8.
183+
184+ISYS firmware ABIs
185+------------------
186+
187+The IPU6 firmware implements a series of ABIs for software access. In general,
188+software firstly prepares the stream configuration ``struct
189+ipu6_fw_isys_stream_cfg_data_abi`` and sends the configuration to firmware via
190+sending ``STREAM_OPEN`` command. Stream configuration includes input pins and
191+output pins, input pin ``struct ipu6_fw_isys_input_pin_info_abi`` defines the
192+resolution and data type of input source, output pin ``struct
193+ipu6_fw_isys_output_pin_info_abi`` defines the output resolution, stride and
194+frame format, etc.
195+
196+Once the driver gets the interrupt from firmware that indicates stream open
197+successfully, the driver will send the ``STREAM_START`` and ``STREAM_CAPTURE``
198+command to request firmware to start capturing image frames. ``STREAM_CAPTURE``
199+command queues the buffers to firmware with ``struct
200+ipu6_fw_isys_frame_buff_set``, software then waits for the interrupt and
201+response from firmware, ``PIN_DATA_READY`` means a buffer is ready on a specific
202+output pin and then software can return the buffer to user.
203+
204+.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do
205+ capture by IPU6 ISYS driver.
Documentation/userspace-api/media/cec/cec-func-open.rst+2−2
@@ -70,5 +70,5 @@ include: · 69 unmodified lines
7070 ``ENOMEM``
7171 Insufficient kernel memory was available.
7272
73−``ENXIO``
74− No device corresponding to this device special file exists.
73+``ENODEV``
74+ Device not found or was removed.
Documentation/userspace-api/media/dvb/frontend_f_open.rst+1−1
@@ -91,7 +91,7 @@ appropriately. · 90 unmodified lines
9191 - The caller has no permission to access the device.
9292
9393 - - ``EBUSY``
94− - The the device driver is already in use.
94+ - The device driver is already in use.
9595
9696 - - ``EMFILE``
9797 - The process already has the maximum number of files open.
Documentation/userspace-api/media/glossary.rst+12−0
@@ -25,6 +25,13 @@ Glossary · 24 unmodified lines
2525
2626 See :ref:`cec`.
2727
28+ Data Unit
29+
30+ Unit of data transported by a bus. On parallel buses, the data unit
31+ consists of one or more related samples while on serial buses the data
32+ unit is logical. If the data unit is image data, it may also be called a
33+ pixel.
34+
2835 Device Driver
2936 Part of the Linux Kernel that implements support for a hardware
3037 component.
@@ -173,6 +180,11 @@ Glossary · 142 unmodified lines
173180 An integrated circuit that integrates all components of a computer
174181 or other electronic systems.
175182
183+ Stream
184+ A distinct flow of data (image data or metadata) from an initial source
185+ to a final sink. The initial source may be e.g. an image sensor and the
186+ final sink e.g. a memory buffer.
187+
176188 V4L2 API
177189 **V4L2 userspace API**
178190
Documentation/userspace-api/media/v4l/dev-meta.rst+21−0
@@ -47,6 +47,12 @@ member of the ``fmt`` union as needed per the desired operation. Both drivers · 46 unmodified lines
4747 and applications must set the remainder of the :c:type:`v4l2_format` structure
4848 to 0.
4949
50+Devices that capture metadata by line have the struct v4l2_fmtdesc
51+``V4L2_FMT_FLAG_META_LINE_BASED`` flag set for :c:func:`VIDIOC_ENUM_FMT`. Such
52+devices can typically also :ref:`capture image data <capture>`. This primarily
53+involves devices that receive the data from a different devices such as a camera
54+sensor.
55+
5056 .. c:type:: v4l2_meta_format
5157
5258 .. tabularcolumns:: |p{1.4cm}|p{2.4cm}|p{13.5cm}|
@@ -65,3 +71,18 @@ to 0. · 12 unmodified lines
6571 - ``buffersize``
6672 - Maximum buffer size in bytes required for data. The value is set by the
6773 driver.
74+ * - __u32
75+ - ``width``
76+ - Width of a line of metadata in Data Units. Valid when
77+ :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set,
78+ otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
79+ * - __u32
80+ - ``height``
81+ - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag
82+ ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
83+ :c:func:`VIDIOC_ENUM_FMT`.
84+ * - __u32
85+ - ``bytesperline``
86+ - Offset in bytes between the beginning of two consecutive lines. Valid
87+ when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is
88+ set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
Documentation/userspace-api/media/v4l/dev-subdev.rst+28−3
@@ -506,6 +506,8 @@ source pads. · 505 unmodified lines
506506
507507 subdev-formats
508508
509+.. _subdev-routing:
510+
509511 Streams, multiplexed media pads and internal routing
510512 ----------------------------------------------------
511513
@@ -527,9 +529,9 @@ the its sink pad and allows to route them individually to one of its source · 15 unmodified lines
527529 pads.
528530
529531 Subdevice drivers that support multiplexed streams are compatible with
530−non-multiplexed subdev drivers, but, of course, require a routing configuration
531−where the link between those two types of drivers contains only a single
532−stream.
532+non-multiplexed subdev drivers. However, if the driver at the sink end of a link
533+does not support streams, then only stream 0 of source end may be captured.
534+There may be additional limitations specific to the sink device.
533535
534536 Understanding streams
535537 ^^^^^^^^^^^^^^^^^^^^^
@@ -570,6 +572,29 @@ Any configurations of a stream within a pad, such as format or selections, · 34 unmodified lines
570572 are independent of similar configurations on other streams. This is
571573 subject to change in the future.
572574
575+Device types and routing setup
576+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
577+
578+Different kinds of sub-devices have differing behaviour for route activation,
579+depending on the hardware. In all cases, however, only routes that have the
580+``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active.
581+
582+Devices generating the streams may allow enabling and disabling some of the
583+routes or have a fixed routing configuration. If the routes can be disabled, not
584+declaring the routes (or declaring them without
585+``VIDIOC_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will
586+disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes
587+back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE``
588+flag unset.
589+
590+Devices transporting the streams almost always have more configurability with
591+respect to routing. Typically any route between the sub-device's sink and source
592+pads is possible, and multiple routes (usually up to certain limited number) may
593+be active simultaneously. For such devices, no routes are created by the driver
594+and user-created routes are fully replaced when ``VIDIOC_SUBDEV_S_ROUTING`` is
595+called on the sub-device. Such newly created routes have the device's default
596+configuration for format and selection rectangles.
597+
573598 Configuring streams
574599 ^^^^^^^^^^^^^^^^^^^
575600
Documentation/userspace-api/media/v4l/func-open.rst+2−2
@@ -65,8 +65,8 @@ EBUSY · 64 unmodified lines
6565 The driver does not support multiple opens and the device is already
6666 in use.
6767
68−ENXIO
69− No device corresponding to this device special file exists.
68+ENODEV
69+ Device not found or was removed.
7070
7171 ENOMEM
7272 Not enough kernel memory was available to complete the request.
Documentation/userspace-api/media/v4l/meta-formats.rst+2−1
@@ -13,9 +13,10 @@ These formats are used for the :ref:`metadata` interface only. · 12 unmodified lines
1313 :maxdepth: 1
1414
1515 metafmt-d4xx
16+ metafmt-generic
1617 metafmt-intel-ipu3
1718 metafmt-rkisp1
1819 metafmt-uvc
20+ metafmt-vivid
1921 metafmt-vsp1-hgo
2022 metafmt-vsp1-hgt
21− metafmt-vivid
Documentation/userspace-api/media/v4l/metafmt-generic.rst+340−0
@@ -0,0 +1,340 @@
1+.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
2+
3+********************************************************************************************************************************************************************************************************************************************************************************
4+V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O')
5+********************************************************************************************************************************************************************************************************************************************************************************
6+
7+
8+Generic line-based metadata formats
9+
10+
11+Description
12+===========
13+
14+These generic line-based metadata formats define the memory layout of the data
15+without defining the format or meaning of the metadata itself.
16+
17+.. _v4l2-meta-fmt-generic-8:
18+
19+V4L2_META_FMT_GENERIC_8
20+-----------------------
21+
22+The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format. This format
23+is used on CSI-2 for 8 bits per :term:`Data Unit`.
24+
25+Additionally it is used for 16 bits per Data Unit when two bytes of metadata are
26+packed into one 16-bit Data Unit. Otherwise the 16 bits per pixel dataformat is
27+:ref:`V4L2_META_FMT_GENERIC_CSI2_16 <v4l2-meta-fmt-generic-csi2-16>`.
28+
29+**Byte Order Of V4L2_META_FMT_GENERIC_8.**
30+Each cell is one byte. "M" denotes a byte of metadata.
31+
32+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|
33+
34+.. flat-table:: Sample 4x2 Metadata Frame
35+ :header-rows: 0
36+ :stub-columns: 0
37+ :widths: 12 8 8 8 8
38+
39+ * - start + 0:
40+ - M\ :sub:`00`
41+ - M\ :sub:`10`
42+ - M\ :sub:`20`
43+ - M\ :sub:`30`
44+ * - start + 4:
45+ - M\ :sub:`01`
46+ - M\ :sub:`11`
47+ - M\ :sub:`21`
48+ - M\ :sub:`31`
49+
50+.. _v4l2-meta-fmt-generic-csi2-10:
51+
52+V4L2_META_FMT_GENERIC_CSI2_10
53+-----------------------------
54+
55+V4L2_META_FMT_GENERIC_CSI2_10 contains 8-bit generic metadata packed in 10-bit
56+Data Units, with one padding byte after every four bytes of metadata. This
57+format is typically used by CSI-2 receivers with a source that transmits
58+MEDIA_BUS_FMT_META_10 and the CSI-2 receiver writes the received data to memory
59+as-is.
60+
61+The packing of the data follows the MIPI CSI-2 specification and the padding of
62+the data is defined in the MIPI CCS specification.
63+
64+This format is also used in conjunction with 20 bits per :term:`Data Unit`
65+formats that pack two bytes of metadata into one Data Unit. Otherwise the
66+20 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_20
67+<v4l2-meta-fmt-generic-csi2-20>`.
68+
69+This format is little endian.
70+
71+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.**
72+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
73+
74+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
75+
76+.. flat-table:: Sample 4x2 Metadata Frame
77+ :header-rows: 0
78+ :stub-columns: 0
79+ :widths: 12 8 8 8 8 8
80+
81+ * - start + 0:
82+ - M\ :sub:`00`
83+ - M\ :sub:`10`
84+ - M\ :sub:`20`
85+ - M\ :sub:`30`
86+ - x
87+ * - start + 5:
88+ - M\ :sub:`01`
89+ - M\ :sub:`11`
90+ - M\ :sub:`21`
91+ - M\ :sub:`31`
92+ - x
93+
94+.. _v4l2-meta-fmt-generic-csi2-12:
95+
96+V4L2_META_FMT_GENERIC_CSI2_12
97+-----------------------------
98+
99+V4L2_META_FMT_GENERIC_CSI2_12 contains 8-bit generic metadata packed in 12-bit
100+Data Units, with one padding byte after every two bytes of metadata. This format
101+is typically used by CSI-2 receivers with a source that transmits
102+MEDIA_BUS_FMT_META_12 and the CSI-2 receiver writes the received data to memory
103+as-is.
104+
105+The packing of the data follows the MIPI CSI-2 specification and the padding of
106+the data is defined in the MIPI CCS specification.
107+
108+This format is also used in conjunction with 24 bits per :term:`Data Unit`
109+formats that pack two bytes of metadata into one Data Unit. Otherwise the
110+24 bits per pixel dataformat is :ref:`V4L2_META_FMT_GENERIC_CSI2_24
111+<v4l2-meta-fmt-generic-csi2-24>`.
112+
113+This format is little endian.
114+
115+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.**
116+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
117+
118+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
119+
120+.. flat-table:: Sample 4x2 Metadata Frame
121+ :header-rows: 0
122+ :stub-columns: 0
123+ :widths: 12 8 8 8 8 8 8
124+
125+ * - start + 0:
126+ - M\ :sub:`00`
127+ - M\ :sub:`10`
128+ - x
129+ - M\ :sub:`20`
130+ - M\ :sub:`30`
131+ - x
132+ * - start + 6:
133+ - M\ :sub:`01`
134+ - M\ :sub:`11`
135+ - x
136+ - M\ :sub:`21`
137+ - M\ :sub:`31`
138+ - x
139+
140+.. _v4l2-meta-fmt-generic-csi2-14:
141+
142+V4L2_META_FMT_GENERIC_CSI2_14
143+-----------------------------
144+
145+V4L2_META_FMT_GENERIC_CSI2_14 contains 8-bit generic metadata packed in 14-bit
146+Data Units, with three padding bytes after every four bytes of metadata. This
147+format is typically used by CSI-2 receivers with a source that transmits
148+MEDIA_BUS_FMT_META_14 and the CSI-2 receiver writes the received data to memory
149+as-is.
150+
151+The packing of the data follows the MIPI CSI-2 specification and the padding of
152+the data is defined in the MIPI CCS specification.
153+
154+This format is little endian.
155+
156+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.**
157+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
158+
159+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}|
160+
161+.. flat-table:: Sample 4x2 Metadata Frame
162+ :header-rows: 0
163+ :stub-columns: 0
164+ :widths: 12 8 8 8 8 8 8 8
165+
166+ * - start + 0:
167+ - M\ :sub:`00`
168+ - M\ :sub:`10`
169+ - M\ :sub:`20`
170+ - M\ :sub:`30`
171+ - x
172+ - x
173+ - x
174+ * - start + 7:
175+ - M\ :sub:`01`
176+ - M\ :sub:`11`
177+ - M\ :sub:`21`
178+ - M\ :sub:`31`
179+ - x
180+ - x
181+ - x
182+
183+.. _v4l2-meta-fmt-generic-csi2-16:
184+
185+V4L2_META_FMT_GENERIC_CSI2_16
186+-----------------------------
187+
188+V4L2_META_FMT_GENERIC_CSI2_16 contains 8-bit generic metadata packed in 16-bit
189+Data Units, with one padding byte after every byte of metadata. This format is
190+typically used by CSI-2 receivers with a source that transmits
191+MEDIA_BUS_FMT_META_16 and the CSI-2 receiver writes the received data to memory
192+as-is.
193+
194+The packing of the data follows the MIPI CSI-2 specification and the padding of
195+the data is defined in the MIPI CCS specification.
196+
197+Some devices support more efficient packing of metadata in conjunction with
198+16-bit image data. In that case the dataformat is
199+:ref:`V4L2_META_FMT_GENERIC_8 <v4l2-meta-fmt-generic-8>`.
200+
201+This format is little endian.
202+
203+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.**
204+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
205+
206+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|
207+
208+.. flat-table:: Sample 4x2 Metadata Frame
209+ :header-rows: 0
210+ :stub-columns: 0
211+ :widths: 12 8 8 8 8 8 8 8 8
212+
213+ * - start + 0:
214+ - M\ :sub:`00`
215+ - x
216+ - M\ :sub:`10`
217+ - x
218+ - M\ :sub:`20`
219+ - x
220+ - M\ :sub:`30`
221+ - x
222+ * - start + 8:
223+ - M\ :sub:`01`
224+ - x
225+ - M\ :sub:`11`
226+ - x
227+ - M\ :sub:`21`
228+ - x
229+ - M\ :sub:`31`
230+ - x
231+
232+.. _v4l2-meta-fmt-generic-csi2-20:
233+
234+V4L2_META_FMT_GENERIC_CSI2_20
235+-----------------------------
236+
237+V4L2_META_FMT_GENERIC_CSI2_20 contains 8-bit generic metadata packed in 20-bit
238+Data Units, with alternating one or two padding bytes after every byte of
239+metadata. This format is typically used by CSI-2 receivers with a source that
240+transmits MEDIA_BUS_FMT_META_20 and the CSI-2 receiver writes the received data
241+to memory as-is.
242+
243+The packing of the data follows the MIPI CSI-2 specification and the padding of
244+the data is defined in the MIPI CCS specification.
245+
246+Some devices support more efficient packing of metadata in conjunction with
247+16-bit image data. In that case the dataformat is
248+:ref:`V4L2_META_FMT_GENERIC_CSI2_10 <v4l2-meta-fmt-generic-csi2-10>`.
249+
250+This format is little endian.
251+
252+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.**
253+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
254+
255+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
256+
257+.. flat-table:: Sample 4x2 Metadata Frame
258+ :header-rows: 0
259+ :stub-columns: 0
260+ :widths: 12 8 8 8 8 8 8 8 8 8 8
261+
262+ * - start + 0:
263+ - M\ :sub:`00`
264+ - x
265+ - M\ :sub:`10`
266+ - x
267+ - x
268+ - M\ :sub:`20`
269+ - x
270+ - M\ :sub:`30`
271+ - x
272+ - x
273+ * - start + 10:
274+ - M\ :sub:`01`
275+ - x
276+ - M\ :sub:`11`
277+ - x
278+ - x
279+ - M\ :sub:`21`
280+ - x
281+ - M\ :sub:`31`
282+ - x
283+ - x
284+
285+.. _v4l2-meta-fmt-generic-csi2-24:
286+
287+V4L2_META_FMT_GENERIC_CSI2_24
288+-----------------------------
289+
290+V4L2_META_FMT_GENERIC_CSI2_24 contains 8-bit generic metadata packed in 24-bit
291+Data Units, with two padding bytes after every byte of metadata. This format is
292+typically used by CSI-2 receivers with a source that transmits
293+MEDIA_BUS_FMT_META_24 and the CSI-2 receiver writes the received data to memory
294+as-is.
295+
296+The packing of the data follows the MIPI CSI-2 specification and the padding of
297+the data is defined in the MIPI CCS specification.
298+
299+Some devices support more efficient packing of metadata in conjunction with
300+16-bit image data. In that case the dataformat is
301+:ref:`V4L2_META_FMT_GENERIC_CSI2_12 <v4l2-meta-fmt-generic-csi2-12>`.
302+
303+This format is little endian.
304+
305+**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.**
306+Each cell is one byte. "M" denotes a byte of metadata and "x" a byte of padding.
307+
308+.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
309+
310+.. flat-table:: Sample 4x2 Metadata Frame
311+ :header-rows: 0
312+ :stub-columns: 0
313+ :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8
314+
315+ * - start + 0:
316+ - M\ :sub:`00`
317+ - x
318+ - x
319+ - M\ :sub:`10`
320+ - x
321+ - x
322+ - M\ :sub:`20`
323+ - x
324+ - x
325+ - M\ :sub:`30`
326+ - x
327+ - x
328+ * - start + 12:
329+ - M\ :sub:`01`
330+ - x
331+ - x
332+ - M\ :sub:`11`
333+ - x
334+ - x
335+ - M\ :sub:`21`
336+ - x
337+ - x
338+ - M\ :sub:`31`
339+ - x
340+ - x
Documentation/userspace-api/media/v4l/mmap.rst+1−1
@@ -188,7 +188,7 @@ Example: Mapping buffers in the multi-planar API · 187 unmodified lines
188188 buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
189189 PROT_READ | PROT_WRITE, /* recommended */
190190 MAP_SHARED, /* recommended */
191− fd, buffer.m.planes[j].m.offset);
191+ fd, buffer.m.planes[j].m.mem_offset);
192192
193193 if (MAP_FAILED == buffers[i].start[j]) {
194194 /* If you do not exit here you should unmap() and free()
Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst+48−0
@@ -36,6 +36,8 @@ are often referred to as greyscale formats. · 35 unmodified lines
3636 - Byte 2
3737 - Byte 3
3838 - Byte 4
39+ - Byte 5
40+ - Byte 6
3941
4042 * .. _V4L2-PIX-FMT-GREY:
4143
@@ -47,6 +49,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
4749 - ...
4850 - ...
4951 - ...
52+ - ...
53+ - ...
5054
5155 * .. _V4L2-PIX-FMT-IPU3-Y10:
5256
@@ -58,6 +62,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
5862 - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [9:6]
5963 - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [9:4]
6064 - Y'\ :sub:`3`\ [9:2]
65+ - ...
66+ - ...
6167
6268 * .. _V4L2-PIX-FMT-Y10:
6369
@@ -69,6 +75,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
6975 - ...
7076 - ...
7177 - ...
78+ - ...
79+ - ...
7280
7381 * .. _V4L2-PIX-FMT-Y10BPACK:
7482
@@ -80,6 +88,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
8088 - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6]
8189 - Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8]
8290 - Y'\ :sub:`3`\ [7:0]
91+ - ...
92+ - ...
8393
8494 * .. _V4L2-PIX-FMT-Y10P:
8595
@@ -91,6 +101,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
91101 - Y'\ :sub:`2`\ [9:2]
92102 - Y'\ :sub:`3`\ [9:2]
93103 - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0]
104+ - ...
105+ - ...
94106
95107 * .. _V4L2-PIX-FMT-Y12:
96108
@@ -102,6 +114,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
102114 - ...
103115 - ...
104116 - ...
117+ - ...
118+ - ...
105119
106120 * .. _V4L2-PIX-FMT-Y012:
107121
@@ -113,6 +127,21 @@ are often referred to as greyscale formats. · 5 unmodified lines
113127 - ...
114128 - ...
115129 - ...
130+ - ...
131+ - ...
132+
133+ * .. _V4L2-PIX-FMT-Y12P:
134+
135+ - ``V4L2_PIX_FMT_Y12P``
136+ - 'Y12P'
137+
138+ - Y'\ :sub:`0`\ [11:4]
139+ - Y'\ :sub:`1`\ [11:4]
140+ - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`0`\ [3:0]
141+ - ...
142+ - ...
143+ - ...
144+ - ...
116145
117146 * .. _V4L2-PIX-FMT-Y14:
118147
@@ -124,6 +153,21 @@ are often referred to as greyscale formats. · 5 unmodified lines
124153 - ...
125154 - ...
126155 - ...
156+ - ...
157+ - ...
158+
159+ * .. _V4L2-PIX-FMT-Y14P:
160+
161+ - ``V4L2_PIX_FMT_Y14P``
162+ - 'Y14P'
163+
164+ - Y'\ :sub:`0`\ [13:6]
165+ - Y'\ :sub:`1`\ [13:6]
166+ - Y'\ :sub:`2`\ [13:6]
167+ - Y'\ :sub:`3`\ [13:6]
168+ - Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [5:0]
169+ - Y'\ :sub:`2`\ [3:0] Y'\ :sub:`1`\ [5:2]
170+ - Y'\ :sub:`3`\ [5:0] Y'\ :sub:`2`\ [5:4]
127171
128172 * .. _V4L2-PIX-FMT-Y16:
129173
@@ -135,6 +179,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
135179 - ...
136180 - ...
137181 - ...
182+ - ...
183+ - ...
138184
139185 * .. _V4L2-PIX-FMT-Y16-BE:
140186
@@ -146,6 +192,8 @@ are often referred to as greyscale formats. · 5 unmodified lines
146192 - ...
147193 - ...
148194 - ...
195+ - ...
196+ - ...
149197
150198 .. raw:: latex
151199
Documentation/userspace-api/media/v4l/subdev-formats.rst+262−7
@@ -33,7 +33,7 @@ Media Bus Formats · 32 unmodified lines
3333 * - __u32
3434 - ``field``
3535 - Field order, from enum :c:type:`v4l2_field`. See
36− :ref:`field-order` for details.
36+ :ref:`field-order` for details. Zero for metadata mbus codes.
3737 * - __u32
3838 - ``colorspace``
3939 - Image colorspace, from enum :c:type:`v4l2_colorspace`.
@@ -45,7 +45,7 @@ Media Bus Formats · 5 unmodified lines
4545 conversion is supported by setting the flag
4646 V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE in the corresponding struct
4747 :c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
48− See :ref:`v4l2-subdev-mbus-code-flags`.
48+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
4949 * - union {
5050 - (anonymous)
5151 * - __u16
@@ -61,7 +61,7 @@ Media Bus Formats · 9 unmodified lines
6161 that ycbcr_enc conversion is supported by setting the flag
6262 V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC in the corresponding struct
6363 :c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
64− See :ref:`v4l2-subdev-mbus-code-flags`.
64+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
6565 * - __u16
6666 - ``hsv_enc``
6767 - HSV encoding, from enum :c:type:`v4l2_hsv_encoding`.
@@ -75,7 +75,7 @@ Media Bus Formats · 7 unmodified lines
7575 that hsv_enc conversion is supported by setting the flag
7676 V4L2_SUBDEV_MBUS_CODE_CSC_HSV_ENC in the corresponding struct
7777 :c:type:`v4l2_subdev_mbus_code_enum` during enumeration.
78− See :ref:`v4l2-subdev-mbus-code-flags`
78+ See :ref:`v4l2-subdev-mbus-code-flags`. Zero for metadata mbus codes.
7979 * - }
8080 -
8181 * - __u16
@@ -90,8 +90,8 @@ Media Bus Formats · 8 unmodified lines
9090 The driver indicates that quantization conversion is supported by
9191 setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION in the
9292 corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
93− during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
94−
93+ during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
94+ metadata mbus codes.
9595 * - __u16
9696 - ``xfer_func``
9797 - Transfer function, from enum :c:type:`v4l2_xfer_func`.
@@ -104,7 +104,8 @@ Media Bus Formats · 6 unmodified lines
104104 The driver indicates that the transfer function conversion is supported by
105105 setting the flag V4L2_SUBDEV_MBUS_CODE_CSC_XFER_FUNC in the
106106 corresponding struct :c:type:`v4l2_subdev_mbus_code_enum`
107− during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`.
107+ during enumeration. See :ref:`v4l2-subdev-mbus-code-flags`. Zero for
108+ metadata mbus codes.
108109 * - __u16
109110 - ``flags``
110111 - flags See: :ref:v4l2-mbus-framefmt-flags
@@ -8306,3 +8307,257 @@ The following table lists the existing metadata formats. · 8195 unmodified lines
83068307 both sides of the link and the bus format is a fixed
83078308 metadata format that is not configurable from userspace.
83088309 Width and height will be set to 0 for this format.
8310+
8311+Generic Serial Metadata Formats
8312+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8313+
8314+Generic serial metadata formats are used on serial buses where the actual data
8315+content is more or less device specific but the data is transmitted and received
8316+by multiple devices that do not process the data in any way, simply writing
8317+it to system memory for processing in software at the end of the pipeline.
8318+
8319+"b" in an array cell signifies a byte of data, followed by the number of the bit
8320+and finally the bit number in subscript. "x" indicates a padding bit.
8321+
8322+.. _media-bus-format-generic-meta:
8323+
8324+.. cssclass: longtable
8325+
8326+.. flat-table:: Generic Serial Metadata Formats
8327+ :header-rows: 2
8328+ :stub-columns: 0
8329+
8330+ * - Identifier
8331+ - Code
8332+ -
8333+ - :cspan:`23` Data organization within bus :term:`Data Unit`
8334+ * -
8335+ -
8336+ - Bit
8337+ - 23
8338+ - 22
8339+ - 21
8340+ - 20
8341+ - 19
8342+ - 18
8343+ - 17
8344+ - 16
8345+ - 15
8346+ - 14
8347+ - 13
8348+ - 12
8349+ - 11
8350+ - 10
8351+ - 9
8352+ - 8
8353+ - 7
8354+ - 6
8355+ - 5
8356+ - 4
8357+ - 3
8358+ - 2
8359+ - 1
8360+ - 0
8361+ * .. _MEDIA-BUS-FMT-META-8:
8362+
8363+ - MEDIA_BUS_FMT_META_8
8364+ - 0x8001
8365+ -
8366+ -
8367+ -
8368+ -
8369+ -
8370+ -
8371+ -
8372+ -
8373+ -
8374+ -
8375+ -
8376+ -
8377+ -
8378+ -
8379+ -
8380+ -
8381+ -
8382+ - b0\ :sub:`7`
8383+ - b0\ :sub:`6`
8384+ - b0\ :sub:`5`
8385+ - b0\ :sub:`4`
8386+ - b0\ :sub:`3`
8387+ - b0\ :sub:`2`
8388+ - b0\ :sub:`1`
8389+ - b0\ :sub:`0`
8390+ * .. _MEDIA-BUS-FMT-META-10:
8391+
8392+ - MEDIA_BUS_FMT_META_10
8393+ - 0x8002
8394+ -
8395+ -
8396+ -
8397+ -
8398+ -
8399+ -
8400+ -
8401+ -
8402+ -
8403+ -
8404+ -
8405+ -
8406+ -
8407+ -
8408+ -
8409+ - b0\ :sub:`7`
8410+ - b0\ :sub:`6`
8411+ - b0\ :sub:`5`
8412+ - b0\ :sub:`4`
8413+ - b0\ :sub:`3`
8414+ - b0\ :sub:`2`
8415+ - b0\ :sub:`1`
8416+ - b0\ :sub:`0`
8417+ - x
8418+ - x
8419+ * .. _MEDIA-BUS-FMT-META-12:
8420+
8421+ - MEDIA_BUS_FMT_META_12
8422+ - 0x8003
8423+ -
8424+ -
8425+ -
8426+ -
8427+ -
8428+ -
8429+ -
8430+ -
8431+ -
8432+ -
8433+ -
8434+ -
8435+ -
8436+ - b0\ :sub:`7`
8437+ - b0\ :sub:`6`
8438+ - b0\ :sub:`5`
8439+ - b0\ :sub:`4`
8440+ - b0\ :sub:`3`
8441+ - b0\ :sub:`2`
8442+ - b0\ :sub:`1`
8443+ - b0\ :sub:`0`
8444+ - x
8445+ - x
8446+ - x
8447+ - x
8448+ * .. _MEDIA-BUS-FMT-META-14:
8449+
8450+ - MEDIA_BUS_FMT_META_14
8451+ - 0x8004
8452+ -
8453+ -
8454+ -
8455+ -
8456+ -
8457+ -
8458+ -
8459+ -
8460+ -
8461+ -
8462+ -
8463+ - b0\ :sub:`7`
8464+ - b0\ :sub:`6`
8465+ - b0\ :sub:`5`
8466+ - b0\ :sub:`4`
8467+ - b0\ :sub:`3`
8468+ - b0\ :sub:`2`
8469+ - b0\ :sub:`1`
8470+ - b0\ :sub:`0`
8471+ - x
8472+ - x
8473+ - x
8474+ - x
8475+ - x
8476+ - x
8477+ * .. _MEDIA-BUS-FMT-META-16:
8478+
8479+ - MEDIA_BUS_FMT_META_16
8480+ - 0x8005
8481+ -
8482+ -
8483+ -
8484+ -
8485+ -
8486+ -
8487+ -
8488+ -
8489+ -
8490+ - b0\ :sub:`7`
8491+ - b0\ :sub:`6`
8492+ - b0\ :sub:`5`
8493+ - b0\ :sub:`4`
8494+ - b0\ :sub:`3`
8495+ - b0\ :sub:`2`
8496+ - b0\ :sub:`1`
8497+ - b0\ :sub:`0`
8498+ - x
8499+ - x
8500+ - x
8501+ - x
8502+ - x
8503+ - x
8504+ - x
8505+ - x
8506+ * .. _MEDIA-BUS-FMT-META-20:
8507+
8508+ - MEDIA_BUS_FMT_META_20
8509+ - 0x8006
8510+ -
8511+ -
8512+ -
8513+ -
8514+ -
8515+ - b0\ :sub:`7`
8516+ - b0\ :sub:`6`
8517+ - b0\ :sub:`5`
8518+ - b0\ :sub:`4`
8519+ - b0\ :sub:`3`
8520+ - b0\ :sub:`2`
8521+ - b0\ :sub:`1`
8522+ - b0\ :sub:`0`
8523+ - x
8524+ - x
8525+ - x
8526+ - x
8527+ - x
8528+ - x
8529+ - x
8530+ - x
8531+ - x
8532+ - x
8533+ - x
8534+ - x
8535+ * .. _MEDIA-BUS-FMT-META-24:
8536+
8537+ - MEDIA_BUS_FMT_META_24
8538+ - 0x8007
8539+ -
8540+ - b0\ :sub:`7`
8541+ - b0\ :sub:`6`
8542+ - b0\ :sub:`5`
8543+ - b0\ :sub:`4`
8544+ - b0\ :sub:`3`
8545+ - b0\ :sub:`2`
8546+ - b0\ :sub:`1`
8547+ - b0\ :sub:`0`
8548+ - x
8549+ - x
8550+ - x
8551+ - x
8552+ - x
8553+ - x
8554+ - x
8555+ - x
8556+ - x
8557+ - x
8558+ - x
8559+ - x
8560+ - x
8561+ - x
8562+ - x
8563+ - x
Documentation/userspace-api/media/v4l/user-func.rst+1−0
@@ -62,6 +62,7 @@ Function Reference · 61 unmodified lines
6262 vidioc-query-dv-timings
6363 vidioc-querystd
6464 vidioc-reqbufs
65+ vidioc-remove-bufs
6566 vidioc-s-hw-freq-seek
6667 vidioc-streamon
6768 vidioc-subdev-enum-frame-interval
Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst+7−0
@@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently: · 226 unmodified lines
227227 The application can ask to configure the quantization of the capture
228228 device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with
229229 :ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set.
230+ * - ``V4L2_FMT_FLAG_META_LINE_BASED``
231+ - 0x0200
232+ - The metadata format is line-based. In this case the ``width``,
233+ ``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are
234+ valid. The buffer consists of ``height`` lines, each having ``width``
235+ Data Units of data and the offset (in bytes) between the beginning of
236+ each two consecutive lines is ``bytesperline``.
230237
231238 Return Value
232239 ============
Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst+86−0
@@ -0,0 +1,86 @@
1+.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
2+.. c:namespace:: V4L
3+
4+.. _VIDIOC_REMOVE_BUFS:
5+
6+************************
7+ioctl VIDIOC_REMOVE_BUFS
8+************************
9+
10+Name
11+====
12+
13+VIDIOC_REMOVE_BUFS - Removes buffers from a queue
14+
15+Synopsis
16+========
17+
18+.. c:macro:: VIDIOC_REMOVE_BUFS
19+
20+``int ioctl(int fd, VIDIOC_REMOVE_BUFS, struct v4l2_remove_buffers *argp)``
21+
22+Arguments
23+=========
24+
25+``fd``
26+ File descriptor returned by :c:func:`open()`.
27+
28+``argp``
29+ Pointer to struct :c:type:`v4l2_remove_buffers`.
30+
31+Description
32+===========
33+
34+Applications can optionally call the :ref:`VIDIOC_REMOVE_BUFS` ioctl to
35+remove buffers from a queue.
36+:ref:`VIDIOC_CREATE_BUFS` ioctl support is mandatory to enable :ref:`VIDIOC_REMOVE_BUFS`.
37+This ioctl is available if the ``V4L2_BUF_CAP_SUPPORTS_REMOVE_BUFS`` capability
38+is set on the queue when :c:func:`VIDIOC_REQBUFS` or :c:func:`VIDIOC_CREATE_BUFS`
39+are invoked.
40+
41+.. c:type:: v4l2_remove_buffers
42+
43+.. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.5cm}|
44+
45+.. flat-table:: struct v4l2_remove_buffers
46+ :header-rows: 0
47+ :stub-columns: 0
48+ :widths: 1 1 2
49+
50+ * - __u32
51+ - ``index``
52+ - The starting buffer index to remove. This field is ignored if count == 0.
53+ * - __u32
54+ - ``count``
55+ - The number of buffers to be removed with indices 'index' until 'index + count - 1'.
56+ All buffers in this range must be valid and in DEQUEUED state.
57+ :ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type`, if it is
58+ invalid it returns ``EINVAL`` error code.
59+ If count is set to 0 :ref:`VIDIOC_REMOVE_BUFS` will do nothing and return 0.
60+ * - __u32
61+ - ``type``
62+ - Type of the stream or buffers, this is the same as the struct
63+ :c:type:`v4l2_format` ``type`` field. See
64+ :c:type:`v4l2_buf_type` for valid values.
65+ * - __u32
66+ - ``reserved``\ [13]
67+ - A place holder for future extensions. Drivers and applications
68+ must set the array to zero.
69+
70+Return Value
71+============
72+
73+On success 0 is returned, on error -1 and the ``errno`` variable is set
74+appropriately. The generic error codes are described at the
75+:ref:`Generic Error Codes <gen-errors>` chapter. If an error occurs, no
76+buffers will be freed and one of the error codes below will be returned:
77+
78+EBUSY
79+ File I/O is in progress.
80+ One or more of the buffers in the range ``index`` to ``index + count - 1`` are not
81+ in DEQUEUED state.
82+
83+EINVAL
84+ One or more of the buffers in the range ``index`` to ``index + count - 1`` do not
85+ exist in the queue.
86+ The buffer type (``type`` field) is not valid.
Documentation/userspace-api/media/v4l/vidioc-reqbufs.rst+1−0
@@ -121,6 +121,7 @@ aborting or finishing any DMA in progress, an implicit · 120 unmodified lines
121121 .. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF:
122122 .. _V4L2-BUF-CAP-SUPPORTS-MMAP-CACHE-HINTS:
123123 .. _V4L2-BUF-CAP-SUPPORTS-MAX-NUM-BUFFERS:
124+.. _V4L2-BUF-CAP-SUPPORTS-REMOVE-BUFS:
124125
125126 .. raw:: latex
126127
Documentation/userspace-api/media/v4l/vidioc-subdev-g-crop.rst+3−3
@@ -37,9 +37,9 @@ Description · 36 unmodified lines
3737
3838 .. note::
3939
40− This is an :ref:`obsolete` interface and may be removed
41− in the future. It is superseded by
42− :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`.
40+ This is an :ref:`obsolete` interface and may be removed in the future. It is
41+ superseded by :ref:`the selection API <VIDIOC_SUBDEV_G_SELECTION>`. No new
42+ extensions to the :c:type:`v4l2_subdev_crop` structure will be accepted.
4343
4444 To retrieve the current crop rectangle applications set the ``pad``
4545 field of a struct :c:type:`v4l2_subdev_crop` to the
Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst+33−18
@@ -43,23 +43,39 @@ The routing configuration determines the flows of data inside an entity. · 42 unmodified lines
4343 Drivers report their current routing tables using the
4444 ``VIDIOC_SUBDEV_G_ROUTING`` ioctl and application may enable or disable routes
4545 with the ``VIDIOC_SUBDEV_S_ROUTING`` ioctl, by adding or removing routes and
46−setting or clearing flags of the ``flags`` field of a
47−struct :c:type:`v4l2_subdev_route`.
46+setting or clearing flags of the ``flags`` field of a struct
47+:c:type:`v4l2_subdev_route`. Similarly to ``VIDIOC_SUBDEV_G_ROUTING``, also
48+``VIDIOC_SUBDEV_S_ROUTING`` returns the routes back to the user.
4849
49−All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called. This
50−means that the userspace must reconfigure all streams after calling the ioctl
51−with e.g. ``VIDIOC_SUBDEV_S_FMT``.
50+All stream configurations are reset when ``VIDIOC_SUBDEV_S_ROUTING`` is called.
51+This means that the userspace must reconfigure all stream formats and selections
52+after calling the ioctl with e.g. ``VIDIOC_SUBDEV_S_FMT``.
5253
5354 Only subdevices which have both sink and source pads can support routing.
5455
55−When inspecting routes through ``VIDIOC_SUBDEV_G_ROUTING`` and the application
56−provided ``num_routes`` is not big enough to contain all the available routes
57−the subdevice exposes, drivers return the ENOSPC error code and adjust the
58−value of the ``num_routes`` field. Application should then reserve enough memory
59−for all the route entries and call ``VIDIOC_SUBDEV_G_ROUTING`` again.
60−
61−On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the
62−``num_routes`` field to reflect the actual number of routes returned.
56+The ``len_routes`` field indicates the number of routes that can fit in the
57+``routes`` array allocated by userspace. It is set by applications for both
58+ioctls to indicate how many routes the kernel can return, and is never modified
59+by the kernel.
60+
61+The ``num_routes`` field indicates the number of routes in the routing
62+table. For ``VIDIOC_SUBDEV_S_ROUTING``, it is set by userspace to the number of
63+routes that the application stored in the ``routes`` array. For both ioctls, it
64+is returned by the kernel and indicates how many routes are stored in the
65+subdevice routing table. This may be smaller or larger than the value of
66+``num_routes`` set by the application for ``VIDIOC_SUBDEV_S_ROUTING``, as
67+drivers may adjust the requested routing table.
68+
69+The kernel can return a ``num_routes`` value larger than ``len_routes`` from
70+both ioctls. This indicates thare are more routes in the routing table than fits
71+the ``routes`` array. In this case, the ``routes`` array is filled by the kernel
72+with the first ``len_routes`` entries of the subdevice routing table. This is
73+not considered to be an error, and the ioctl call succeeds. If the applications
74+wants to retrieve the missing routes, it can issue a new
75+``VIDIOC_SUBDEV_G_ROUTING`` call with a large enough ``routes`` array.
76+
77+``VIDIOC_SUBDEV_S_ROUTING`` may return more routes than the user provided in
78+``num_routes`` field due to e.g. hardware properties.
6379
6480 .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}|
6581
@@ -74,6 +90,9 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the · 8 unmodified lines
7490 - ``which``
7591 - Routing table to be accessed, from enum
7692 :ref:`v4l2_subdev_format_whence <v4l2-subdev-format-whence>`.
93+ * - __u32
94+ - ``len_routes``
95+ - The length of the array (as in memory reserved for the array)
7796 * - struct :c:type:`v4l2_subdev_route`
7897 - ``routes[]``
7998 - Array of struct :c:type:`v4l2_subdev_route` entries
@@ -81,7 +100,7 @@ On a successful ``VIDIOC_SUBDEV_G_ROUTING`` call the driver updates the · 1 unmodified line
81100 - ``num_routes``
82101 - Number of entries of the routes array
83102 * - __u32
84− - ``reserved``\ [5]
103+ - ``reserved``\ [11]
85104 - Reserved for future extensions. Applications and drivers must set
86105 the array to zero.
87106
@@ -135,10 +154,6 @@ On success 0 is returned, on error -1 and the ``errno`` variable is set · 47 unmodified lines
135154 appropriately. The generic error codes are described at the
136155 :ref:`Generic Error Codes <gen-errors>` chapter.
137156
138−ENOSPC
139− The application provided ``num_routes`` is not big enough to contain
140− all the available routes the subdevice exposes.
141−
142157 EINVAL
143158 The sink or source pad identifiers reference a non-existing pad or reference
144159 pads of different types (ie. the sink_pad identifiers refers to a source
Documentation/userspace-api/media/videodev2.h.rst.exceptions+1−0
@@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags · 214 unmodified lines
215215 replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags
216216 replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags
217217 replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags
218+replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags
218219
219220 # V4L2 timecode types
220221 replace define V4L2_TC_TYPE_24FPS timecode-type
MAINTAINERS+18−1
@@ -4110,6 +4110,13 @@ N: bcm113* · 4109 unmodified lines
41104110 N: bcm216*
41114111 N: kona
41124112
4113+BROADCOM BCM2835 CAMERA DRIVERS
4114+M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
4115+L: linux-media@vger.kernel.org
4116+S: Maintained
4117+F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml
4118+F: drivers/media/platform/broadcom/bcm2835-unicam*
4119+
41134120 BROADCOM BCM47XX MIPS ARCHITECTURE
41144121 M: Hauke Mehrtens <hauke@hauke-m.de>
41154122 M: Rafał Miłecki <zajec5@gmail.com>
@@ -11091,6 +11098,16 @@ F: Documentation/admin-guide/media/ipu3_rcb.svg · 6975 unmodified lines
1109111098 F: Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
1109211099 F: drivers/staging/media/ipu3/
1109311100
11101+INTEL IPU6 INPUT SYSTEM DRIVER
11102+M: Sakari Ailus <sakari.ailus@linux.intel.com>
11103+M: Bingbu Cao <bingbu.cao@intel.com>
11104+R: Tianshu Qiu <tian.shu.qiu@intel.com>
11105+L: linux-media@vger.kernel.org
11106+S: Maintained
11107+T: git git://linuxtv.org/media_tree.git
11108+F: Documentation/admin-guide/media/ipu6-isys.rst
11109+F: drivers/media/pci/intel/ipu6/
11110+
1109411111 INTEL ISHTP ECLITE DRIVER
1109511112 M: Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
1109611113 L: platform-driver-x86@vger.kernel.org
@@ -16539,7 +16556,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com> · 5442 unmodified lines
1653916556 L: linux-media@vger.kernel.org
1654016557 S: Maintained
1654116558 T: git git://linuxtv.org/media_tree.git
16542−F: Documentation/devicetree/bindings/media/i2c/ov8856.yaml
16559+F: Documentation/devicetree/bindings/media/i2c/ovti,ov8856.yaml
1654316560 F: drivers/media/i2c/ov8856.c
1654416561
1654516562 OMNIVISION OV8858 SENSOR DRIVER
drivers/media/cec/core/cec-adap.c+20−4
@@ -490,6 +490,15 @@ int cec_thread_func(void *_adap) · 489 unmodified lines
490490 goto unlock;
491491 }
492492
493+ if (adap->transmit_in_progress &&
494+ adap->transmit_in_progress_aborted) {
495+ if (adap->transmitting)
496+ cec_data_cancel(adap->transmitting,
497+ CEC_TX_STATUS_ABORTED, 0);
498+ adap->transmit_in_progress = false;
499+ adap->transmit_in_progress_aborted = false;
500+ goto unlock;
501+ }
493502 if (adap->transmit_in_progress && timeout) {
494503 /*
495504 * If we timeout, then log that. Normally this does
@@ -771,6 +780,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, · 275 unmodified lines
771780 {
772781 struct cec_data *data;
773782 bool is_raw = msg_is_raw(msg);
783+ int err;
774784
775785 if (adap->devnode.unregistered)
776786 return -ENODEV;
@@ -935,11 +945,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, · 158 unmodified lines
935945 * Release the lock and wait, retake the lock afterwards.
936946 */
937947 mutex_unlock(&adap->lock);
938− wait_for_completion_killable(&data->c);
939− if (!data->completed)
940− cancel_delayed_work_sync(&data->work);
948+ err = wait_for_completion_killable(&data->c);
949+ cancel_delayed_work_sync(&data->work);
941950 mutex_lock(&adap->lock);
942951
952+ if (err)
953+ adap->transmit_in_progress_aborted = true;
954+
943955 /* Cancel the transmit if it was interrupted */
944956 if (!data->completed) {
945957 if (data->msg.tx_status & CEC_TX_STATUS_OK)
@@ -1575,9 +1587,12 @@ unconfigure: · 629 unmodified lines
15751587 */
15761588 static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
15771589 {
1578− if (WARN_ON(adap->is_configuring || adap->is_configured))
1590+ if (WARN_ON(adap->is_claiming_log_addrs ||
1591+ adap->is_configuring || adap->is_configured))
15791592 return;
15801593
1594+ adap->is_claiming_log_addrs = true;
1595+
15811596 init_completion(&adap->config_completion);
15821597
15831598 /* Ready to kick off the thread */
@@ -1592,6 +1607,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) · 8 unmodified lines
15921607 wait_for_completion(&adap->config_completion);
15931608 mutex_lock(&adap->lock);
15941609 }
1610+ adap->is_claiming_log_addrs = false;
15951611 }
15961612
15971613 /*
drivers/media/cec/core/cec-api.c+4−1
@@ -178,7 +178,7 @@ static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh, · 177 unmodified lines
178178 CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU |
179179 CEC_LOG_ADDRS_FL_CDC_ONLY;
180180 mutex_lock(&adap->lock);
181− if (!adap->is_configuring &&
181+ if (!adap->is_claiming_log_addrs && !adap->is_configuring &&
182182 (!log_addrs.num_log_addrs || !adap->is_configured) &&
183183 !cec_is_busy(adap, fh)) {
184184 err = __cec_s_log_addrs(adap, &log_addrs, block);
@@ -664,6 +664,8 @@ static int cec_release(struct inode *inode, struct file *filp) · 479 unmodified lines
664664 list_del_init(&data->xfer_list);
665665 }
666666 mutex_unlock(&adap->lock);
667+
668+ mutex_lock(&fh->lock);
667669 while (!list_empty(&fh->msgs)) {
668670 struct cec_msg_entry *entry =
669671 list_first_entry(&fh->msgs, struct cec_msg_entry, list);
@@ -681,6 +683,7 @@ static int cec_release(struct inode *inode, struct file *filp) · 11 unmodified lines
681683 kfree(entry);
682684 }
683685 }
686+ mutex_unlock(&fh->lock);
684687 kfree(fh);
685688
686689 cec_put_device(devnode);
drivers/media/cec/core/cec-core.c+2−2
@@ -62,12 +62,12 @@ int cec_get_device(struct cec_devnode *devnode) · 61 unmodified lines
6262 */
6363 mutex_lock(&devnode->lock);
6464 /*
65− * return ENXIO if the cec device has been removed
65+ * return ENODEV if the cec device has been removed
6666 * already or if it is not registered anymore.
6767 */
6868 if (!devnode->registered) {
6969 mutex_unlock(&devnode->lock);
70− return -ENXIO;
70+ return -ENODEV;
7171 }
7272 /* and increase the device refcount */
7373 get_device(&devnode->dev);
drivers/media/cec/platform/cros-ec/cros-ec-cec.c+8−1
@@ -8,6 +8,7 @@ · 7 unmodified lines
88
99 #include <linux/kernel.h>
1010 #include <linux/module.h>
11+#include <linux/mod_devicetable.h>
1112 #include <linux/platform_device.h>
1213 #include <linux/dmi.h>
1314 #include <linux/pci.h>
@@ -573,6 +574,12 @@ static void cros_ec_cec_remove(struct platform_device *pdev) · 559 unmodified lines
573574 }
574575 }
575576
577+static const struct platform_device_id cros_ec_cec_id[] = {
578+ { DRV_NAME, 0 },
579+ {}
580+};
581+MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
582+
576583 static struct platform_driver cros_ec_cec_driver = {
577584 .probe = cros_ec_cec_probe,
578585 .remove_new = cros_ec_cec_remove,
@@ -580,6 +587,7 @@ static struct platform_driver cros_ec_cec_driver = { · 1 unmodified line
580587 .name = DRV_NAME,
581588 .pm = &cros_ec_cec_pm_ops,
582589 },
590+ .id_table = cros_ec_cec_id,
583591 };
584592
585593 module_platform_driver(cros_ec_cec_driver);
@@ -587,4 +595,3 @@ module_platform_driver(cros_ec_cec_driver); · 1 unmodified line
587595 MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
588596 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
589597 MODULE_LICENSE("GPL");
590−MODULE_ALIAS("platform:" DRV_NAME);
drivers/media/common/saa7146/saa7146_hlp.c+4−4
@@ -122,7 +122,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev, · 121 unmodified lines
122122 xacm = 0;
123123
124124 /* set horizontal filter parameters (CXY = CXUV) */
125− cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff;
125+ cxy = hps_h_coeff_tab[min(xpsc - 1, 63)].hps_coeff;
126126 cxuv = cxy;
127127
128128 /* calculate and set horizontal fine scale (xsci) */
@@ -151,7 +151,7 @@ static int calculate_h_scale_registers(struct saa7146_dev *dev, · 22 unmodified lines
151151 xacm = 0;
152152 /* get best match in the table of attenuations
153153 for horizontal scaling */
154− h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum;
154+ h_atten = hps_h_coeff_tab[min(xpsc - 1, 63)].weight_sum;
155155
156156 for (i = 0; h_attenuation[i] != 0; i++) {
157157 if (h_attenuation[i] >= h_atten)
@@ -283,10 +283,10 @@ static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field · 125 unmodified lines
283283 }
284284
285285 /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */
286− cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff;
286+ cya_cyb = hps_v_coeff_tab[min(yacl, 63)].hps_coeff;
287287
288288 /* get best match in the table of attenuations for vertical scaling */
289− v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum;
289+ v_atten = hps_v_coeff_tab[min(yacl, 63)].weight_sum;
290290
291291 for (i = 0; v_attenuation[i] != 0; i++) {
292292 if (v_attenuation[i] >= v_atten)
drivers/media/common/videobuf2/videobuf2-core.c+163−68
@@ -421,11 +421,12 @@ static void init_buffer_cache_hints(struct vb2_queue *q, struct vb2_buffer *vb) · 420 unmodified lines
421421 */
422422 static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, unsigned int index)
423423 {
424− WARN_ON(index >= q->max_num_buffers || q->bufs[index] || vb->vb2_queue);
424+ WARN_ON(index >= q->max_num_buffers || test_bit(index, q->bufs_bitmap) || vb->vb2_queue);
425425
426426 q->bufs[index] = vb;
427427 vb->index = index;
428428 vb->vb2_queue = q;
429+ set_bit(index, q->bufs_bitmap);
429430 }
430431
431432 /**
@@ -434,6 +435,7 @@ static void vb2_queue_add_buffer(struct vb2_queue *q, struct vb2_buffer *vb, uns · 2 unmodified lines
434435 */
435436 static void vb2_queue_remove_buffer(struct vb2_buffer *vb)
436437 {
438+ clear_bit(vb->index, vb->vb2_queue->bufs_bitmap);
437439 vb->vb2_queue->bufs[vb->index] = NULL;
438440 vb->vb2_queue = NULL;
439441 }
@@ -442,16 +444,19 @@ static void vb2_queue_remove_buffer(struct vb2_buffer *vb) · 2 unmodified lines
442444 * __vb2_queue_alloc() - allocate vb2 buffer structures and (for MMAP type)
443445 * video buffer memory for all buffers/planes on the queue and initializes the
444446 * queue
447+ * @first_index: index of the first created buffer, all newly allocated buffers
448+ * have indices in the range [first_index..first_index+count-1]
445449 *
446450 * Returns the number of buffers successfully allocated.
447451 */
448452 static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
449453 unsigned int num_buffers, unsigned int num_planes,
450− const unsigned plane_sizes[VB2_MAX_PLANES])
454+ const unsigned int plane_sizes[VB2_MAX_PLANES],
455+ unsigned int *first_index)
451456 {
452− unsigned int q_num_buffers = vb2_get_num_buffers(q);
453457 unsigned int buffer, plane;
454458 struct vb2_buffer *vb;
459+ unsigned long index = q->max_num_buffers;
455460 int ret;
456461
457462 /*
@@ -459,7 +464,25 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line
459464 * in the queue is below q->max_num_buffers
460465 */
461466 num_buffers = min_t(unsigned int, num_buffers,
462− q->max_num_buffers - q_num_buffers);
467+ q->max_num_buffers - vb2_get_num_buffers(q));
468+
469+ while (num_buffers) {
470+ index = bitmap_find_next_zero_area(q->bufs_bitmap, q->max_num_buffers,
471+ 0, num_buffers, 0);
472+
473+ if (index < q->max_num_buffers)
474+ break;
475+ /* Try to find free space for less buffers */
476+ num_buffers--;
477+ }
478+
479+ /* If there is no space left to allocate buffers return 0 to indicate the error */
480+ if (!num_buffers) {
481+ *first_index = 0;
482+ return 0;
483+ }
484+
485+ *first_index = index;
463486
464487 for (buffer = 0; buffer < num_buffers; ++buffer) {
465488 /* Allocate vb2 buffer structures */
@@ -479,7 +502,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 13 unmodified lines
479502 vb->planes[plane].min_length = plane_sizes[plane];
480503 }
481504
482− vb2_queue_add_buffer(q, vb, q_num_buffers + buffer);
505+ vb2_queue_add_buffer(q, vb, index++);
483506 call_void_bufop(q, init_buffer, vb);
484507
485508 /* Allocate video buffer memory for the MMAP type */
@@ -517,17 +540,16 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, · 31 unmodified lines
517540 }
518541
519542 /*
520− * __vb2_free_mem() - release all video buffer memory for a given queue
543+ * __vb2_free_mem() - release video buffer memory for a given range of
544+ * buffers in a given queue
521545 */
522−static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
546+static void __vb2_free_mem(struct vb2_queue *q, unsigned int start, unsigned int count)
523547 {
524− unsigned int buffer;
548+ unsigned int i;
525549 struct vb2_buffer *vb;
526− unsigned int q_num_buffers = vb2_get_num_buffers(q);
527550
528− for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
529− ++buffer) {
530− vb = vb2_get_buffer(q, buffer);
551+ for (i = start; i < start + count; i++) {
552+ vb = vb2_get_buffer(q, i);
531553 if (!vb)
532554 continue;
533555
@@ -542,35 +564,33 @@ static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) · 8 unmodified lines
542564 }
543565
544566 /*
545− * __vb2_queue_free() - free buffers at the end of the queue - video memory and
567+ * __vb2_queue_free() - free @count buffers from @start index of the queue - video memory and
546568 * related information, if no buffers are left return the queue to an
547569 * uninitialized state. Might be called even if the queue has already been freed.
548570 */
549−static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
571+static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned int count)
550572 {
551− unsigned int buffer;
552− unsigned int q_num_buffers = vb2_get_num_buffers(q);
573+ unsigned int i;
553574
554575 lockdep_assert_held(&q->mmap_lock);
555576
556577 /* Call driver-provided cleanup function for each buffer, if provided */
557− for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
558− ++buffer) {
559− struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
578+ for (i = start; i < start + count; i++) {
579+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
560580
561581 if (vb && vb->planes[0].mem_priv)
562582 call_void_vb_qop(vb, buf_cleanup, vb);
563583 }
564584
565585 /* Release video buffer memory */
566− __vb2_free_mem(q, buffers);
586+ __vb2_free_mem(q, start, count);
567587
568588 #ifdef CONFIG_VIDEO_ADV_DEBUG
569589 /*
570590 * Check that all the calls were balanced during the life-time of this
571591 * queue. If not then dump the counters to the kernel log.
572592 */
573− if (q_num_buffers) {
593+ if (vb2_get_num_buffers(q)) {
574594 bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
575595 q->cnt_prepare_streaming != q->cnt_unprepare_streaming ||
576596 q->cnt_wait_prepare != q->cnt_wait_finish;
@@ -596,8 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 19 unmodified lines
596616 q->cnt_stop_streaming = 0;
597617 q->cnt_unprepare_streaming = 0;
598618 }
599− for (buffer = 0; buffer < vb2_get_num_buffers(q); buffer++) {
600− struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
619+ for (i = start; i < start + count; i++) {
620+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
601621 bool unbalanced;
602622
603623 if (!vb)
@@ -614,7 +634,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 10 unmodified lines
614634
615635 if (unbalanced) {
616636 pr_info("unbalanced counters for queue %p, buffer %d:\n",
617− q, buffer);
637+ q, i);
618638 if (vb->cnt_buf_init != vb->cnt_buf_cleanup)
619639 pr_info(" buf_init: %u buf_cleanup: %u\n",
620640 vb->cnt_buf_init, vb->cnt_buf_cleanup);
@@ -648,9 +668,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 27 unmodified lines
648668 #endif
649669
650670 /* Free vb2 buffers */
651− for (buffer = q_num_buffers - buffers; buffer < q_num_buffers;
652− ++buffer) {
653− struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
671+ for (i = start; i < start + count; i++) {
672+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
654673
655674 if (!vb)
656675 continue;
@@ -659,7 +678,6 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) · 2 unmodified lines
659678 kfree(vb);
660679 }
661680
662− q->num_buffers -= buffers;
663681 if (!vb2_get_num_buffers(q)) {
664682 q->memory = VB2_MEMORY_UNKNOWN;
665683 INIT_LIST_HEAD(&q->queued_list);
@@ -691,7 +709,7 @@ EXPORT_SYMBOL(vb2_buffer_in_use); · 25 unmodified lines
691709 static bool __buffers_in_use(struct vb2_queue *q)
692710 {
693711 unsigned int buffer;
694− for (buffer = 0; buffer < vb2_get_num_buffers(q); ++buffer) {
712+ for (buffer = 0; buffer < q->max_num_buffers; ++buffer) {
695713 struct vb2_buffer *vb = vb2_get_buffer(q, buffer);
696714
697715 if (!vb)
@@ -813,6 +831,32 @@ static bool verify_coherency_flags(struct vb2_queue *q, bool non_coherent_mem) · 115 unmodified lines
813831 return true;
814832 }
815833
834+static int vb2_core_allocated_buffers_storage(struct vb2_queue *q)
835+{
836+ if (!q->bufs)
837+ q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
838+ if (!q->bufs)
839+ return -ENOMEM;
840+
841+ if (!q->bufs_bitmap)
842+ q->bufs_bitmap = bitmap_zalloc(q->max_num_buffers, GFP_KERNEL);
843+ if (!q->bufs_bitmap) {
844+ kfree(q->bufs);
845+ q->bufs = NULL;
846+ return -ENOMEM;
847+ }
848+
849+ return 0;
850+}
851+
852+static void vb2_core_free_buffers_storage(struct vb2_queue *q)
853+{
854+ kfree(q->bufs);
855+ q->bufs = NULL;
856+ bitmap_free(q->bufs_bitmap);
857+ q->bufs_bitmap = NULL;
858+}
859+
816860 int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
817861 unsigned int flags, unsigned int *count)
818862 {
@@ -820,7 +864,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line
820864 unsigned int q_num_bufs = vb2_get_num_buffers(q);
821865 unsigned plane_sizes[VB2_MAX_PLANES] = { };
822866 bool non_coherent_mem = flags & V4L2_MEMORY_FLAG_NON_COHERENT;
823− unsigned int i;
867+ unsigned int i, first_index;
824868 int ret = 0;
825869
826870 if (q->streaming) {
@@ -851,9 +895,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 24 unmodified lines
851895 * queued without ever calling STREAMON.
852896 */
853897 __vb2_queue_cancel(q);
854− __vb2_queue_free(q, q_num_bufs);
898+ __vb2_queue_free(q, 0, q->max_num_buffers);
855899 mutex_unlock(&q->mmap_lock);
856900
901+ q->is_busy = 0;
857902 /*
858903 * In case of REQBUFS(0) return immediately without calling
859904 * driver's queue_setup() callback and allocating resources.
@@ -865,7 +910,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 5 unmodified lines
865910 /*
866911 * Make sure the requested values and current defaults are sane.
867912 */
868− num_buffers = max_t(unsigned int, *count, q->min_queued_buffers);
913+ num_buffers = max_t(unsigned int, *count, q->min_reqbufs_allocation);
869914 num_buffers = min_t(unsigned int, num_buffers, q->max_num_buffers);
870915 memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
871916 /*
@@ -873,10 +918,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line
873918 * in the queue_setup op.
874919 */
875920 mutex_lock(&q->mmap_lock);
876− if (!q->bufs)
877− q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
878− if (!q->bufs)
879− ret = -ENOMEM;
921+ ret = vb2_core_allocated_buffers_storage(q);
880922 q->memory = memory;
881923 mutex_unlock(&q->mmap_lock);
882924 if (ret)
@@ -906,8 +948,10 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 23 unmodified lines
906948
907949 /* Finally, allocate buffers and video memory */
908950 allocated_buffers =
909− __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
951+ __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes, &first_index);
910952 if (allocated_buffers == 0) {
953+ /* There shouldn't be any buffers allocated, so first_index == 0 */
954+ WARN_ON(first_index);
911955 dprintk(q, 1, "memory allocation failed\n");
912956 ret = -ENOMEM;
913957 goto error;
@@ -917,7 +961,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 3 unmodified lines
917961 * There is no point in continuing if we can't allocate the minimum
918962 * number of buffers needed by this vb2_queue.
919963 */
920− if (allocated_buffers < q->min_queued_buffers)
964+ if (allocated_buffers < q->min_reqbufs_allocation)
921965 ret = -ENOMEM;
922966
923967 /*
@@ -946,7 +990,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 22 unmodified lines
946990 }
947991
948992 mutex_lock(&q->mmap_lock);
949− q->num_buffers = allocated_buffers;
950993
951994 if (ret < 0) {
952995 /*
@@ -954,7 +997,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line
954997 * from already queued buffers and it will reset q->memory to
955998 * VB2_MEMORY_UNKNOWN.
956999 */
957− __vb2_queue_free(q, allocated_buffers);
1000+ __vb2_queue_free(q, first_index, allocated_buffers);
9581001 mutex_unlock(&q->mmap_lock);
9591002 return ret;
9601003 }
@@ -966,6 +1009,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, · 5 unmodified lines
9661009 */
9671010 *count = allocated_buffers;
9681011 q->waiting_for_buffers = !q->is_output;
1012+ q->is_busy = 1;
9691013
9701014 return 0;
9711015
@@ -973,6 +1017,7 @@ error: · 1 unmodified line
9731017 mutex_lock(&q->mmap_lock);
9741018 q->memory = VB2_MEMORY_UNKNOWN;
9751019 mutex_unlock(&q->mmap_lock);
1020+ vb2_core_free_buffers_storage(q);
9761021 return ret;
9771022 }
9781023 EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
@@ -980,7 +1025,8 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs); · 1 unmodified line
9801025 int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
9811026 unsigned int flags, unsigned int *count,
9821027 unsigned int requested_planes,
983− const unsigned int requested_sizes[])
1028+ const unsigned int requested_sizes[],
1029+ unsigned int *first_index)
9841030 {
9851031 unsigned int num_planes = 0, num_buffers, allocated_buffers;
9861032 unsigned plane_sizes[VB2_MAX_PLANES] = { };
@@ -1005,11 +1051,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 18 unmodified lines
10051051 * value in the queue_setup op.
10061052 */
10071053 mutex_lock(&q->mmap_lock);
1054+ ret = vb2_core_allocated_buffers_storage(q);
10081055 q->memory = memory;
1009− if (!q->bufs)
1010− q->bufs = kcalloc(q->max_num_buffers, sizeof(*q->bufs), GFP_KERNEL);
1011− if (!q->bufs)
1012− ret = -ENOMEM;
10131056 mutex_unlock(&q->mmap_lock);
10141057 if (ret)
10151058 return ret;
@@ -1042,7 +1085,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 26 unmodified lines
10421085
10431086 /* Finally, allocate buffers and video memory */
10441087 allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
1045− num_planes, plane_sizes);
1088+ num_planes, plane_sizes, first_index);
10461089 if (allocated_buffers == 0) {
10471090 dprintk(q, 1, "memory allocation failed\n");
10481091 ret = -ENOMEM;
@@ -1072,7 +1115,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 23 unmodified lines
10721115 }
10731116
10741117 mutex_lock(&q->mmap_lock);
1075− q->num_buffers += allocated_buffers;
10761118
10771119 if (ret < 0) {
10781120 /*
@@ -1080,7 +1122,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 1 unmodified line
10801122 * from already queued buffers and it will reset q->memory to
10811123 * VB2_MEMORY_UNKNOWN.
10821124 */
1083− __vb2_queue_free(q, allocated_buffers);
1125+ __vb2_queue_free(q, *first_index, allocated_buffers);
10841126 mutex_unlock(&q->mmap_lock);
10851127 return -ENOMEM;
10861128 }
@@ -1091,6 +1133,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, · 4 unmodified lines
10911133 * to the userspace.
10921134 */
10931135 *count = allocated_buffers;
1136+ q->is_busy = 1;
10941137
10951138 return 0;
10961139
@@ -1648,6 +1691,44 @@ int vb2_core_prepare_buf(struct vb2_queue *q, struct vb2_buffer *vb, void *pb) · 551 unmodified lines
16481691 }
16491692 EXPORT_SYMBOL_GPL(vb2_core_prepare_buf);
16501693
1694+int vb2_core_remove_bufs(struct vb2_queue *q, unsigned int start, unsigned int count)
1695+{
1696+ unsigned int i, ret = 0;
1697+ unsigned int q_num_bufs = vb2_get_num_buffers(q);
1698+
1699+ if (count == 0)
1700+ return 0;
1701+
1702+ if (count > q_num_bufs)
1703+ return -EINVAL;
1704+
1705+ if (start > q->max_num_buffers - count)
1706+ return -EINVAL;
1707+
1708+ mutex_lock(&q->mmap_lock);
1709+
1710+ /* Check that all buffers in the range exist */
1711+ for (i = start; i < start + count; i++) {
1712+ struct vb2_buffer *vb = vb2_get_buffer(q, i);
1713+
1714+ if (!vb) {
1715+ ret = -EINVAL;
1716+ goto unlock;
1717+ }
1718+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
1719+ ret = -EBUSY;
1720+ goto unlock;
1721+ }
1722+ }
1723+ __vb2_queue_free(q, start, count);
1724+ dprintk(q, 2, "%u buffers removed\n", count);
1725+
1726+unlock:
1727+ mutex_unlock(&q->mmap_lock);
1728+ return ret;
1729+}
1730+EXPORT_SYMBOL_GPL(vb2_core_remove_bufs);
1731+
16511732 /*
16521733 * vb2_start_streaming() - Attempt to start streaming.
16531734 * @q: videobuf2 queue
@@ -1694,7 +1775,7 @@ static int vb2_start_streaming(struct vb2_queue *q) · 40 unmodified lines
16941775 * Forcefully reclaim buffers if the driver did not
16951776 * correctly return them to vb2.
16961777 */
1697− for (i = 0; i < vb2_get_num_buffers(q); ++i) {
1778+ for (i = 0; i < q->max_num_buffers; ++i) {
16981779 vb = vb2_get_buffer(q, i);
16991780
17001781 if (!vb)
@@ -2100,7 +2181,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 399 unmodified lines
21002181 * to vb2 in stop_streaming().
21012182 */
21022183 if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
2103− for (i = 0; i < vb2_get_num_buffers(q); i++) {
2184+ for (i = 0; i < q->max_num_buffers; i++) {
21042185 struct vb2_buffer *vb = vb2_get_buffer(q, i);
21052186
21062187 if (!vb)
@@ -2144,7 +2225,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) · 37 unmodified lines
21442225 * call to __fill_user_buffer() after buf_finish(). That order can't
21452226 * be changed, so we can't move the buf_finish() to __vb2_dqbuf().
21462227 */
2147− for (i = 0; i < vb2_get_num_buffers(q); i++) {
2228+ for (i = 0; i < q->max_num_buffers; i++) {
21482229 struct vb2_buffer *vb;
21492230 struct media_request *req;
21502231
@@ -2503,7 +2584,7 @@ int vb2_core_queue_init(struct vb2_queue *q) · 352 unmodified lines
25032584 WARN_ON(!q->ops->buf_queue))
25042585 return -EINVAL;
25052586
2506− if (WARN_ON(q->max_num_buffers > MAX_BUFFER_INDEX) ||
2587+ if (WARN_ON(q->max_num_buffers < VB2_MAX_FRAME) ||
25072588 WARN_ON(q->min_queued_buffers > q->max_num_buffers))
25082589 return -EINVAL;
25092590
@@ -2521,6 +2602,25 @@ int vb2_core_queue_init(struct vb2_queue *q) · 11 unmodified lines
25212602 if (WARN_ON(q->supports_requests && q->min_queued_buffers))
25222603 return -EINVAL;
25232604
2605+ /*
2606+ * The minimum requirement is 2: one buffer is used
2607+ * by the hardware while the other is being processed by userspace.
2608+ */
2609+ if (q->min_reqbufs_allocation < 2)
2610+ q->min_reqbufs_allocation = 2;
2611+
2612+ /*
2613+ * If the driver needs 'min_queued_buffers' in the queue before
2614+ * calling start_streaming() then the minimum requirement is
2615+ * 'min_queued_buffers + 1' to keep at least one buffer available
2616+ * for userspace.
2617+ */
2618+ if (q->min_reqbufs_allocation < q->min_queued_buffers + 1)
2619+ q->min_reqbufs_allocation = q->min_queued_buffers + 1;
2620+
2621+ if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers))
2622+ return -EINVAL;
2623+
25242624 INIT_LIST_HEAD(&q->queued_list);
25252625 INIT_LIST_HEAD(&q->done_list);
25262626 spin_lock_init(&q->done_lock);
@@ -2552,9 +2652,9 @@ void vb2_core_queue_release(struct vb2_queue *q) · 25 unmodified lines
25522652 __vb2_cleanup_fileio(q);
25532653 __vb2_queue_cancel(q);
25542654 mutex_lock(&q->mmap_lock);
2555− __vb2_queue_free(q, vb2_get_num_buffers(q));
2556− kfree(q->bufs);
2557− q->bufs = NULL;
2655+ __vb2_queue_free(q, 0, q->max_num_buffers);
2656+ vb2_core_free_buffers_storage(q);
2657+ q->is_busy = 0;
25582658 mutex_unlock(&q->mmap_lock);
25592659 }
25602660 EXPORT_SYMBOL_GPL(vb2_core_queue_release);
@@ -2713,7 +2813,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 152 unmodified lines
27132813 struct vb2_fileio_data *fileio;
27142814 struct vb2_buffer *vb;
27152815 int i, ret;
2716− unsigned int count = 0;
27172816
27182817 /*
27192818 * Sanity check
@@ -2734,18 +2833,8 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 14 unmodified lines
27342833 if (q->streaming || vb2_get_num_buffers(q) > 0)
27352834 return -EBUSY;
27362835
2737− /*
2738− * Start with q->min_queued_buffers + 1, driver can increase it in
2739− * queue_setup()
2740− *
2741− * 'min_queued_buffers' buffers need to be queued up before you
2742− * can start streaming, plus 1 for userspace (or in this case,
2743− * kernelspace) processing.
2744− */
2745− count = max(2, q->min_queued_buffers + 1);
2746−
27472836 dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
2748− (read) ? "read" : "write", count, q->fileio_read_once,
2837+ (read) ? "read" : "write", q->min_reqbufs_allocation, q->fileio_read_once,
27492838 q->fileio_write_immediately);
27502839
27512840 fileio = kzalloc(sizeof(*fileio), GFP_KERNEL);
@@ -2759,13 +2848,19 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) · 7 unmodified lines
27592848 * Request buffers and use MMAP type to force driver
27602849 * to allocate buffers by itself.
27612850 */
2762− fileio->count = count;
2851+ fileio->count = q->min_reqbufs_allocation;
27632852 fileio->memory = VB2_MEMORY_MMAP;
27642853 fileio->type = q->type;
27652854 q->fileio = fileio;
27662855 ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count);
27672856 if (ret)
27682857 goto err_kfree;
2858+ /* vb2_fileio_data supports max VB2_MAX_FRAME buffers */
2859+ if (fileio->count > VB2_MAX_FRAME) {
2860+ dprintk(q, 1, "fileio: more than VB2_MAX_FRAME buffers requested\n");
2861+ ret = -ENOSPC;
2862+ goto err_reqbufs;
2863+ }
27692864
27702865 /*
27712866 * Userspace can never add or delete buffers later, so there
drivers/media/common/videobuf2/videobuf2-v4l2.c+28−6
@@ -685,7 +685,7 @@ static void vb2_set_flags_and_caps(struct vb2_queue *q, u32 memory, · 684 unmodified lines
685685 *flags &= V4L2_MEMORY_FLAG_NON_COHERENT;
686686 }
687687
688− *caps = V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
688+ *caps |= V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS;
689689 if (q->io_modes & VB2_MMAP)
690690 *caps |= V4L2_BUF_CAP_SUPPORTS_MMAP;
691691 if (q->io_modes & VB2_USERPTR)
@@ -795,11 +795,15 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) · 103 unmodified lines
795795 for (i = 0; i < requested_planes; i++)
796796 if (requested_sizes[i] == 0)
797797 return -EINVAL;
798− return ret ? ret : vb2_core_create_bufs(q, create->memory,
799− create->flags,
800− &create->count,
801− requested_planes,
802− requested_sizes);
798+ if (ret)
799+ return ret;
800+
801+ return vb2_core_create_bufs(q, create->memory,
802+ create->flags,
803+ &create->count,
804+ requested_planes,
805+ requested_sizes,
806+ &create->index);
803807 }
804808 EXPORT_SYMBOL_GPL(vb2_create_bufs);
805809
@@ -997,6 +1001,24 @@ EXPORT_SYMBOL_GPL(vb2_poll); · 191 unmodified lines
9971001
9981002 /* vb2 ioctl helpers */
9991003
1004+int vb2_ioctl_remove_bufs(struct file *file, void *priv,
1005+ struct v4l2_remove_buffers *d)
1006+{
1007+ struct video_device *vdev = video_devdata(file);
1008+
1009+ if (vdev->queue->type != d->type)
1010+ return -EINVAL;
1011+
1012+ if (d->count == 0)
1013+ return 0;
1014+
1015+ if (vb2_queue_is_busy(vdev->queue, file))
1016+ return -EBUSY;
1017+
1018+ return vb2_core_remove_bufs(vdev->queue, d->index, d->count);
1019+}
1020+EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs);
1021+
10001022 int vb2_ioctl_reqbufs(struct file *file, void *priv,
10011023 struct v4l2_requestbuffers *p)
10021024 {
drivers/media/dvb-core/dvbdev.c+1−1
@@ -956,7 +956,7 @@ int dvb_usercopy(struct file *file, · 955 unmodified lines
956956 int (*func)(struct file *file,
957957 unsigned int cmd, void *arg))
958958 {
959− char sbuf[128];
959+ char sbuf[128] = {};
960960 void *mbuf = NULL;
961961 void *parg = NULL;
962962 int err = -EINVAL;
drivers/media/dvb-frontends/as102_fe_types.h+1−1
@@ -174,6 +174,6 @@ struct as10x_register_addr { · 173 unmodified lines
174174 uint32_t addr;
175175 /* register mode access */
176176 uint8_t mode;
177−};
177+} __packed;
178178
179179 #endif
drivers/media/dvb-frontends/cxd2880/Kconfig+1−1
@@ -5,4 +5,4 @@ config DVB_CXD2880 · 4 unmodified lines
55 depends on DVB_CORE && SPI
66 default m if !MEDIA_SUBDRV_AUTOSELECT
77 help
8− Say Y when you want to support this frontend.
8+ Say Y when you want to support this frontend.
drivers/media/dvb-frontends/drx39xyj/drx_driver.h+0−2
@@ -33,7 +33,6 @@ · 32 unmodified lines
3333
3434 #include <linux/kernel.h>
3535 #include <linux/errno.h>
36−#include <linux/firmware.h>
3736 #include <linux/i2c.h>
3837
3938 /*
@@ -1910,7 +1909,6 @@ struct drx_demod_instance { · 1870 unmodified lines
19101909 /* generic demodulator data */
19111910
19121911 struct i2c_adapter *i2c;
1913− const struct firmware *firmware;
19141912 };
19151913
19161914 /*-------------------------------------------------------------------------
drivers/media/dvb-frontends/drx39xyj/drxj.c+26−32
@@ -56,6 +56,7 @@ INCLUDE FILES · 55 unmodified lines
5656 #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
5757
5858 #include <linux/module.h>
59+#include <linux/firmware.h>
5960 #include <linux/init.h>
6061 #include <linux/string.h>
6162 #include <linux/slab.h>
@@ -1444,8 +1445,7 @@ static int drxdap_fasi_read_block(struct i2c_device_addr *dev_addr, · 1382 unmodified lines
14441445
14451446 /* Read block from I2C **************************************************** */
14461447 do {
1447− u16 todo = (datasize < DRXDAP_MAX_RCHUNKSIZE ?
1448− datasize : DRXDAP_MAX_RCHUNKSIZE);
1448+ u16 todo = min(datasize, DRXDAP_MAX_RCHUNKSIZE);
14491449
14501450 bufx = 0;
14511451
@@ -1659,7 +1659,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, · 207 unmodified lines
16591659 Address must be rewritten because HI is reset after data transport and
16601660 expects an address.
16611661 */
1662− todo = (block_size < datasize ? block_size : datasize);
1662+ todo = min(block_size, datasize);
16631663 if (todo == 0) {
16641664 u16 overhead_size_i2c_addr = 0;
16651665 u16 data_block_size = 0;
@@ -1681,9 +1681,7 @@ static int drxdap_fasi_write_block(struct i2c_device_addr *dev_addr, · 15 unmodified lines
16811681 first_err = st;
16821682 }
16831683 bufx = 0;
1684− todo =
1685− (data_block_size <
1686− datasize ? data_block_size : datasize);
1684+ todo = min(data_block_size, datasize);
16871685 }
16881686 memcpy(&buf[bufx], data, todo);
16891687 /* write (address if can do and) data */
@@ -11750,6 +11748,7 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 10060 unmodified lines
1175011748 u8 *mc_data = NULL;
1175111749 unsigned size;
1175211750 char *mc_file;
11751+ const struct firmware *fw;
1175311752
1175411753 /* Check arguments */
1175511754 if (!mc_info || !mc_info->mc_file)
@@ -11757,28 +11756,22 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 1 unmodified line
1175711756
1175811757 mc_file = mc_info->mc_file;
1175911758
11760− if (!demod->firmware) {
11761− const struct firmware *fw = NULL;
11762−
11763− rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
11764− if (rc < 0) {
11765− pr_err("Couldn't read firmware %s\n", mc_file);
11766− return rc;
11767− }
11768− demod->firmware = fw;
11769−
11770− if (demod->firmware->size < 2 * sizeof(u16)) {
11771− rc = -EINVAL;
11772− pr_err("Firmware is too short!\n");
11773− goto release;
11774− }
11759+ rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
11760+ if (rc < 0) {
11761+ pr_err("Couldn't read firmware %s\n", mc_file);
11762+ return rc;
11763+ }
1177511764
11776− pr_info("Firmware %s, size %zu\n",
11777− mc_file, demod->firmware->size);
11765+ if (fw->size < 2 * sizeof(u16)) {
11766+ rc = -EINVAL;
11767+ pr_err("Firmware is too short!\n");
11768+ goto release;
1177811769 }
1177911770
11780− mc_data_init = demod->firmware->data;
11781− size = demod->firmware->size;
11771+ pr_info("Firmware %s, size %zu\n", mc_file, fw->size);
11772+
11773+ mc_data_init = fw->data;
11774+ size = fw->size;
1178211775
1178311776 mc_data = (void *)mc_data_init;
1178411777 /* Check data */
@@ -11874,7 +11867,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 89 unmodified lines
1187411867 0x0000)) {
1187511868 pr_err("error reading firmware at pos %zd\n",
1187611869 mc_data - mc_data_init);
11877− return -EIO;
11870+ rc = -EIO;
11871+ goto release;
1187811872 }
1187911873
1188011874 result = memcmp(curr_ptr, mc_data_buffer,
@@ -11883,7 +11877,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 2 unmodified lines
1188311877 if (result) {
1188411878 pr_err("error verifying firmware at pos %zd\n",
1188511879 mc_data - mc_data_init);
11886− return -EIO;
11880+ rc = -EIO;
11881+ goto release;
1188711882 }
1188811883
1188911884 curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
@@ -11893,17 +11888,17 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, · 3 unmodified lines
1189311888 break;
1189411889 }
1189511890 default:
11896− return -EINVAL;
11891+ rc = -EINVAL;
11892+ goto release;
1189711893
1189811894 }
1189911895 mc_data += mc_block_nr_bytes;
1190011896 }
1190111897
11902− return 0;
11898+ rc = 0;
1190311899
1190411900 release:
11905− release_firmware(demod->firmware);
11906− demod->firmware = NULL;
11901+ release_firmware(fw);
1190711902
1190811903 return rc;
1190911904 }
@@ -12271,7 +12266,6 @@ static void drx39xxj_release(struct dvb_frontend *fe) · 361 unmodified lines
1227112266 kfree(demod->my_ext_attr);
1227212267 kfree(demod->my_common_attr);
1227312268 kfree(demod->my_i2c_dev_addr);
12274− release_firmware(demod->firmware);
1227512269 kfree(demod);
1227612270 kfree(state);
1227712271 }
drivers/media/dvb-frontends/lgdt3306a.c+5−0
@@ -2176,6 +2176,11 @@ static int lgdt3306a_probe(struct i2c_client *client) · 2175 unmodified lines
21762176 struct dvb_frontend *fe;
21772177 int ret;
21782178
2179+ if (!client->dev.platform_data) {
2180+ dev_err(&client->dev, "platform data is mandatory\n");
2181+ return -EINVAL;
2182+ }
2183+
21792184 config = kmemdup(client->dev.platform_data,
21802185 sizeof(struct lgdt3306a_config), GFP_KERNEL);
21812186 if (config == NULL) {
drivers/media/dvb-frontends/m88ds3103.c+7−0
@@ -1000,6 +1000,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) · 999 unmodified lines
10001000 if (ret)
10011001 goto err;
10021002
1003+ if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) {
1004+ /* to light up the LOCK led */
1005+ ret = m88ds3103_update_bits(dev, 0x11, 0x80, 0x00);
1006+ if (ret)
1007+ goto err;
1008+ }
1009+
10031010 dev->delivery_system = c->delivery_system;
10041011
10051012 return 0;
drivers/media/dvb-frontends/mxl5xx.c+11−11
@@ -1381,57 +1381,57 @@ static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id, · 1380 unmodified lines
13811381 u32 nco_count_min = 0;
13821382 u32 clk_type = 0;
13831383
1384− struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
1384+ static const struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
13851385 {0x90700010, 8, 1}, {0x90700010, 9, 1},
13861386 {0x90700010, 10, 1}, {0x90700010, 11, 1},
13871387 {0x90700010, 12, 1}, {0x90700010, 13, 1},
13881388 {0x90700010, 14, 1}, {0x90700010, 15, 1} };
1389− struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
1389+ static const struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
13901390 {0x90700010, 16, 1}, {0x90700010, 17, 1},
13911391 {0x90700010, 18, 1}, {0x90700010, 19, 1},
13921392 {0x90700010, 20, 1}, {0x90700010, 21, 1},
13931393 {0x90700010, 22, 1}, {0x90700010, 23, 1} };
1394− struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
1394+ static const struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
13951395 {0x90700014, 0, 1}, {0x90700014, 1, 1},
13961396 {0x90700014, 2, 1}, {0x90700014, 3, 1},
13971397 {0x90700014, 4, 1}, {0x90700014, 5, 1},
13981398 {0x90700014, 6, 1}, {0x90700014, 7, 1} };
1399− struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
1399+ static const struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
14001400 {0x90700018, 0, 3}, {0x90700018, 4, 3},
14011401 {0x90700018, 8, 3}, {0x90700018, 12, 3},
14021402 {0x90700018, 16, 3}, {0x90700018, 20, 3},
14031403 {0x90700018, 24, 3}, {0x90700018, 28, 3} };
1404− struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
1404+ static const struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
14051405 {0x9070000C, 16, 1}, {0x9070000C, 17, 1},
14061406 {0x9070000C, 18, 1}, {0x9070000C, 19, 1},
14071407 {0x9070000C, 20, 1}, {0x9070000C, 21, 1},
14081408 {0x9070000C, 22, 1}, {0x9070000C, 23, 1} };
1409− struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
1409+ static const struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
14101410 {0x90700010, 0, 1}, {0x90700010, 1, 1},
14111411 {0x90700010, 2, 1}, {0x90700010, 3, 1},
14121412 {0x90700010, 4, 1}, {0x90700010, 5, 1},
14131413 {0x90700010, 6, 1}, {0x90700010, 7, 1} };
1414− struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
1414+ static const struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
14151415 {0x9070000C, 0, 1}, {0x9070000C, 1, 1},
14161416 {0x9070000C, 2, 1}, {0x9070000C, 3, 1},
14171417 {0x9070000C, 4, 1}, {0x9070000C, 5, 1},
14181418 {0x9070000C, 6, 1}, {0x9070000C, 7, 1} };
1419− struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
1419+ static const struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
14201420 {0x9070000C, 24, 1}, {0x9070000C, 25, 1},
14211421 {0x9070000C, 26, 1}, {0x9070000C, 27, 1},
14221422 {0x9070000C, 28, 1}, {0x9070000C, 29, 1},
14231423 {0x9070000C, 30, 1}, {0x9070000C, 31, 1} };
1424− struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
1424+ static const struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
14251425 {0x90700014, 8, 1}, {0x90700014, 9, 1},
14261426 {0x90700014, 10, 1}, {0x90700014, 11, 1},
14271427 {0x90700014, 12, 1}, {0x90700014, 13, 1},
14281428 {0x90700014, 14, 1}, {0x90700014, 15, 1} };
1429− struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
1429+ static const struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
14301430 {0x907001D4, 0, 1}, {0x907001D4, 1, 1},
14311431 {0x907001D4, 2, 1}, {0x907001D4, 3, 1},
14321432 {0x907001D4, 4, 1}, {0x907001D4, 5, 1},
14331433 {0x907001D4, 6, 1}, {0x907001D4, 7, 1} };
1434− struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
1434+ static const struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
14351435 {0x90700044, 16, 80}, {0x90700044, 16, 81},
14361436 {0x90700044, 16, 82}, {0x90700044, 16, 83},
14371437 {0x90700044, 16, 84}, {0x90700044, 16, 85},
drivers/media/dvb-frontends/si2165.c+2−4
@@ -513,10 +513,8 @@ static int si2165_upload_firmware(struct si2165_state *state) · 512 unmodified lines
513513 ret = 0;
514514 state->firmware_loaded = true;
515515 error:
516− if (fw) {
517− release_firmware(fw);
518− fw = NULL;
519− }
516+ release_firmware(fw);
517+ fw = NULL;
520518
521519 return ret;
522520 }
drivers/media/dvb-frontends/stb0899_drv.c+1−1
@@ -1277,7 +1277,7 @@ static int stb0899_get_dev_id(struct stb0899_state *state) · 1276 unmodified lines
12771277 dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
12781278 CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
12791279 fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
1280− if (! (chip_id > 0)) {
1280+ if (!chip_id) {
12811281 dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
12821282
12831283 return -ENODEV;
drivers/media/dvb-frontends/tda10048.c+6−3
@@ -410,6 +410,7 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) · 409 unmodified lines
410410 struct tda10048_config *config = &state->config;
411411 int i;
412412 u32 if_freq_khz;
413+ u64 sample_freq;
413414
414415 dprintk(1, "%s(bw = %d)\n", __func__, bw);
415416
@@ -451,9 +452,11 @@ static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) · 35 unmodified lines
451452 dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor);
452453
453454 /* Calculate the sample frequency */
454− state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45);
455− state->sample_freq /= (state->pll_nfactor + 1);
456− state->sample_freq /= (state->pll_pfactor + 4);
455+ sample_freq = state->xtal_hz;
456+ sample_freq *= state->pll_mfactor + 45;
457+ do_div(sample_freq, state->pll_nfactor + 1);
458+ do_div(sample_freq, state->pll_pfactor + 4);
459+ state->sample_freq = sample_freq;
457460 dprintk(1, "- sample_freq = %d\n", state->sample_freq);
458461
459462 /* Update the I/F */
drivers/media/dvb-frontends/tda18271c2dd.c+2−2
@@ -328,7 +328,7 @@ static int CalcMainPLL(struct tda_state *state, u32 freq) · 327 unmodified lines
328328
329329 OscFreq = (u64) freq * (u64) Div;
330330 OscFreq *= (u64) 16384;
331− do_div(OscFreq, (u64)16000000);
331+ do_div(OscFreq, 16000000);
332332 MainDiv = OscFreq;
333333
334334 state->m_Regs[MPD] = PostDiv & 0x77;
@@ -352,7 +352,7 @@ static int CalcCalPLL(struct tda_state *state, u32 freq) · 17 unmodified lines
352352 OscFreq = (u64)freq * (u64)Div;
353353 /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */
354354 OscFreq *= (u64)16384;
355− do_div(OscFreq, (u64)16000000);
355+ do_div(OscFreq, 16000000);
356356 CalDiv = OscFreq;
357357
358358 state->m_Regs[CPD] = PostDiv;
drivers/media/i2c/Kconfig+2−0
@@ -195,6 +195,7 @@ config VIDEO_IMX334 · 194 unmodified lines
195195 config VIDEO_IMX335
196196 tristate "Sony IMX335 sensor support"
197197 depends on OF_GPIO
198+ select V4L2_CCI_I2C
198199 help
199200 This is a Video4Linux2 sensor driver for the Sony
200201 IMX335 camera.
@@ -405,6 +406,7 @@ config VIDEO_OV2740 · 204 unmodified lines
405406 config VIDEO_OV4689
406407 tristate "OmniVision OV4689 sensor support"
407408 depends on GPIOLIB
409+ select V4L2_CCI_I2C
408410 help
409411 This is a Video4Linux2 sensor-level driver for the OmniVision
410412 OV4689 camera.
drivers/media/i2c/adv7180.c+1−1
@@ -1486,7 +1486,7 @@ static int adv7180_probe(struct i2c_client *client) · 1485 unmodified lines
14861486 if (ret)
14871487 goto err_media_entity_cleanup;
14881488
1489− if (state->irq) {
1489+ if (state->irq > 0) {
14901490 ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
14911491 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
14921492 KBUILD_MODNAME, state);
drivers/media/i2c/adv748x/adv748x-hdmi.c+8−8
@@ -214,7 +214,7 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, · 213 unmodified lines
214214 * v4l2_subdev_video_ops
215215 */
216216
217−static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd,
217+static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
218218 struct v4l2_dv_timings *timings)
219219 {
220220 struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -254,7 +254,7 @@ error: · 33 unmodified lines
254254 return ret;
255255 }
256256
257−static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd,
257+static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
258258 struct v4l2_dv_timings *timings)
259259 {
260260 struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -269,7 +269,7 @@ static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, · 8 unmodified lines
269269 return 0;
270270 }
271271
272−static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd,
272+static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
273273 struct v4l2_dv_timings *timings)
274274 {
275275 struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd);
@@ -392,9 +392,6 @@ static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd, · 116 unmodified lines
392392 }
393393
394394 static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
395− .s_dv_timings = adv748x_hdmi_s_dv_timings,
396− .g_dv_timings = adv748x_hdmi_g_dv_timings,
397− .query_dv_timings = adv748x_hdmi_query_dv_timings,
398395 .g_input_status = adv748x_hdmi_g_input_status,
399396 .s_stream = adv748x_hdmi_s_stream,
400397 .g_pixelaspect = adv748x_hdmi_g_pixelaspect,
@@ -413,7 +410,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) · 12 unmodified lines
413410 if (!tx)
414411 return -ENOLINK;
415412
416− adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings);
413+ adv748x_hdmi_query_dv_timings(&hdmi->sd, 0, &timings);
417414
418415 return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock);
419416 }
@@ -610,6 +607,9 @@ static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = { · 190 unmodified lines
610607 .get_fmt = adv748x_hdmi_get_format,
611608 .get_edid = adv748x_hdmi_get_edid,
612609 .set_edid = adv748x_hdmi_set_edid,
610+ .s_dv_timings = adv748x_hdmi_s_dv_timings,
611+ .g_dv_timings = adv748x_hdmi_g_dv_timings,
612+ .query_dv_timings = adv748x_hdmi_query_dv_timings,
613613 .dv_timings_cap = adv748x_hdmi_dv_timings_cap,
614614 .enum_dv_timings = adv748x_hdmi_enum_dv_timings,
615615 };
@@ -734,7 +734,7 @@ int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) · 118 unmodified lines
734734 struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30;
735735 int ret;
736736
737− adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720);
737+ adv748x_hdmi_s_dv_timings(&hdmi->sd, 0, &cea1280x720);
738738
739739 /* Initialise a default 16:9 aspect ratio */
740740 hdmi->aspect_ratio.numerator = 16;
drivers/media/i2c/adv7511-v4l2.c+11−5
@@ -995,8 +995,8 @@ static int adv7511_s_stream(struct v4l2_subdev *sd, int enable) · 994 unmodified lines
995995 return 0;
996996 }
997997
998−static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
999− struct v4l2_dv_timings *timings)
998+static int adv7511_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
999+ struct v4l2_dv_timings *timings)
10001000 {
10011001 struct adv7511_state *state = get_adv7511_state(sd);
10021002 struct v4l2_bt_timings *bt = &timings->bt;
@@ -1004,6 +1004,9 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, · 1 unmodified line
10041004
10051005 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
10061006
1007+ if (pad != 0)
1008+ return -EINVAL;
1009+
10071010 /* quick sanity check */
10081011 if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL))
10091012 return -EINVAL;
@@ -1042,13 +1045,16 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd, · 32 unmodified lines
10421045 return 0;
10431046 }
10441047
1045−static int adv7511_g_dv_timings(struct v4l2_subdev *sd,
1048+static int adv7511_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
10461049 struct v4l2_dv_timings *timings)
10471050 {
10481051 struct adv7511_state *state = get_adv7511_state(sd);
10491052
10501053 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
10511054
1055+ if (pad != 0)
1056+ return -EINVAL;
1057+
10521058 if (!timings)
10531059 return -EINVAL;
10541060
@@ -1078,8 +1084,6 @@ static int adv7511_dv_timings_cap(struct v4l2_subdev *sd, · 23 unmodified lines
10781084
10791085 static const struct v4l2_subdev_video_ops adv7511_video_ops = {
10801086 .s_stream = adv7511_s_stream,
1081− .s_dv_timings = adv7511_s_dv_timings,
1082− .g_dv_timings = adv7511_g_dv_timings,
10831087 };
10841088
10851089 /* ------------------------------ AUDIO OPS ------------------------------ */
@@ -1403,6 +1407,8 @@ static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { · 317 unmodified lines
14031407 .enum_mbus_code = adv7511_enum_mbus_code,
14041408 .get_fmt = adv7511_get_fmt,
14051409 .set_fmt = adv7511_set_fmt,
1410+ .s_dv_timings = adv7511_s_dv_timings,
1411+ .g_dv_timings = adv7511_g_dv_timings,
14061412 .enum_dv_timings = adv7511_enum_dv_timings,
14071413 .dv_timings_cap = adv7511_dv_timings_cap,
14081414 };
drivers/media/i2c/adv7604.c+10−10
@@ -1557,8 +1557,8 @@ static unsigned int adv76xx_read_hdmi_pixelclock(struct v4l2_subdev *sd) · 1556 unmodified lines
15571557 return freq;
15581558 }
15591559
1560−static int adv76xx_query_dv_timings(struct v4l2_subdev *sd,
1561− struct v4l2_dv_timings *timings)
1560+static int adv76xx_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
1561+ struct v4l2_dv_timings *timings)
15621562 {
15631563 struct adv76xx_state *state = to_state(sd);
15641564 const struct adv76xx_chip_info *info = state->info;
@@ -1687,8 +1687,8 @@ found: · 122 unmodified lines
16871687 return 0;
16881688 }
16891689
1690−static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
1691− struct v4l2_dv_timings *timings)
1690+static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
1691+ struct v4l2_dv_timings *timings)
16921692 {
16931693 struct adv76xx_state *state = to_state(sd);
16941694 struct v4l2_bt_timings *bt;
@@ -1730,8 +1730,8 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd, · 35 unmodified lines
17301730 return 0;
17311731 }
17321732
1733−static int adv76xx_g_dv_timings(struct v4l2_subdev *sd,
1734− struct v4l2_dv_timings *timings)
1733+static int adv76xx_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
1734+ struct v4l2_dv_timings *timings)
17351735 {
17361736 struct adv76xx_state *state = to_state(sd);
17371737
@@ -2607,7 +2607,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) · 869 unmodified lines
26072607 stdi.lcf, stdi.bl, stdi.lcvs,
26082608 stdi.interlaced ? "interlaced" : "progressive",
26092609 stdi.hs_pol, stdi.vs_pol);
2610− if (adv76xx_query_dv_timings(sd, &timings))
2610+ if (adv76xx_query_dv_timings(sd, 0, &timings))
26112611 v4l2_info(sd, "No video detected\n");
26122612 else
26132613 v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -2726,9 +2726,6 @@ static const struct v4l2_subdev_core_ops adv76xx_core_ops = { · 112 unmodified lines
27262726 static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
27272727 .s_routing = adv76xx_s_routing,
27282728 .g_input_status = adv76xx_g_input_status,
2729− .s_dv_timings = adv76xx_s_dv_timings,
2730− .g_dv_timings = adv76xx_g_dv_timings,
2731− .query_dv_timings = adv76xx_query_dv_timings,
27322729 };
27332730
27342731 static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
@@ -2738,6 +2735,9 @@ static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = { · 3 unmodified lines
27382735 .set_fmt = adv76xx_set_format,
27392736 .get_edid = adv76xx_get_edid,
27402737 .set_edid = adv76xx_set_edid,
2738+ .s_dv_timings = adv76xx_s_dv_timings,
2739+ .g_dv_timings = adv76xx_g_dv_timings,
2740+ .query_dv_timings = adv76xx_query_dv_timings,
27412741 .dv_timings_cap = adv76xx_dv_timings_cap,
27422742 .enum_dv_timings = adv76xx_enum_dv_timings,
27432743 };
drivers/media/i2c/adv7842.c+17−8
@@ -1518,7 +1518,7 @@ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, · 1517 unmodified lines
15181518 timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS;
15191519 }
15201520
1521−static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
1521+static int adv7842_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
15221522 struct v4l2_dv_timings *timings)
15231523 {
15241524 struct adv7842_state *state = to_state(sd);
@@ -1527,6 +1527,9 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, · 2 unmodified lines
15271527
15281528 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
15291529
1530+ if (pad != 0)
1531+ return -EINVAL;
1532+
15301533 memset(timings, 0, sizeof(struct v4l2_dv_timings));
15311534
15321535 /* SDP block */
@@ -1643,7 +1646,7 @@ found: · 110 unmodified lines
16431646 return 0;
16441647 }
16451648
1646−static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
1649+static int adv7842_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
16471650 struct v4l2_dv_timings *timings)
16481651 {
16491652 struct adv7842_state *state = to_state(sd);
@@ -1652,6 +1655,9 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, · 2 unmodified lines
16521655
16531656 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
16541657
1658+ if (pad != 0)
1659+ return -EINVAL;
1660+
16551661 if (state->mode == ADV7842_MODE_SDP)
16561662 return -ENODATA;
16571663
@@ -1689,11 +1695,14 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, · 31 unmodified lines
16891695 return 0;
16901696 }
16911697
1692−static int adv7842_g_dv_timings(struct v4l2_subdev *sd,
1698+static int adv7842_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
16931699 struct v4l2_dv_timings *timings)
16941700 {
16951701 struct adv7842_state *state = to_state(sd);
16961702
1703+ if (pad != 0)
1704+ return -EINVAL;
1705+
16971706 if (state->mode == ADV7842_MODE_SDP)
16981707 return -ENODATA;
16991708 *timings = state->timings;
@@ -2780,7 +2789,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) · 1080 unmodified lines
27802789 "interlaced" : "progressive",
27812790 hs_pol, vs_pol);
27822791 }
2783− if (adv7842_query_dv_timings(sd, &timings))
2792+ if (adv7842_query_dv_timings(sd, 0, &timings))
27842793 v4l2_info(sd, "No video detected\n");
27852794 else
27862795 v4l2_print_dv_timings(sd->name, "Detected format: ",
@@ -3226,7 +3235,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) · 439 unmodified lines
32263235
32273236 memset(&state->timings, 0, sizeof(struct v4l2_dv_timings));
32283237
3229− adv7842_s_dv_timings(sd, &timings);
3238+ adv7842_s_dv_timings(sd, 0, &timings);
32303239
32313240 return ret;
32323241 }
@@ -3298,9 +3307,6 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { · 65 unmodified lines
32983307 .s_routing = adv7842_s_routing,
32993308 .querystd = adv7842_querystd,
33003309 .g_input_status = adv7842_g_input_status,
3301− .s_dv_timings = adv7842_s_dv_timings,
3302− .g_dv_timings = adv7842_g_dv_timings,
3303− .query_dv_timings = adv7842_query_dv_timings,
33043310 };
33053311
33063312 static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
@@ -3309,6 +3315,9 @@ static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { · 2 unmodified lines
33093315 .set_fmt = adv7842_set_format,
33103316 .get_edid = adv7842_get_edid,
33113317 .set_edid = adv7842_set_edid,
3318+ .s_dv_timings = adv7842_s_dv_timings,
3319+ .g_dv_timings = adv7842_g_dv_timings,
3320+ .query_dv_timings = adv7842_query_dv_timings,
33123321 .enum_dv_timings = adv7842_enum_dv_timings,
33133322 .dv_timings_cap = adv7842_dv_timings_cap,
33143323 };
drivers/media/i2c/dw9714.c+3−3
@@ -310,8 +310,8 @@ module_i2c_driver(dw9714_i2c_driver); · 309 unmodified lines
310310
311311 MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
312312 MODULE_AUTHOR("Jian Xu Zheng");
313−MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
314−MODULE_AUTHOR("Jouni Ukkonen <jouni.ukkonen@intel.com>");
315−MODULE_AUTHOR("Tommi Franttila <tommi.franttila@intel.com>");
313+MODULE_AUTHOR("Yuning Pu");
314+MODULE_AUTHOR("Jouni Ukkonen");
315+MODULE_AUTHOR("Tommi Franttila");
316316 MODULE_DESCRIPTION("DW9714 VCM driver");
317317 MODULE_LICENSE("GPL v2");
drivers/media/i2c/et8ek8/et8ek8_driver.c+2−2
@@ -1475,7 +1475,7 @@ err_mutex: · 1474 unmodified lines
14751475 return ret;
14761476 }
14771477
1478−static void __exit et8ek8_remove(struct i2c_client *client)
1478+static void et8ek8_remove(struct i2c_client *client)
14791479 {
14801480 struct v4l2_subdev *subdev = i2c_get_clientdata(client);
14811481 struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev);
@@ -1517,7 +1517,7 @@ static struct i2c_driver et8ek8_i2c_driver = { · 35 unmodified lines
15171517 .of_match_table = et8ek8_of_table,
15181518 },
15191519 .probe = et8ek8_probe,
1520− .remove = __exit_p(et8ek8_remove),
1520+ .remove = et8ek8_remove,
15211521 .id_table = et8ek8_id_table,
15221522 };
15231523
drivers/media/i2c/hi556.c+93−12
@@ -3,10 +3,13 @@ · 2 unmodified lines
33
44 #include <asm/unaligned.h>
55 #include <linux/acpi.h>
6+#include <linux/clk.h>
67 #include <linux/delay.h>
8+#include <linux/gpio/consumer.h>
79 #include <linux/i2c.h>
810 #include <linux/module.h>
911 #include <linux/pm_runtime.h>
12+#include <linux/regulator/consumer.h>
1013 #include <media/v4l2-ctrls.h>
1114 #include <media/v4l2-device.h>
1215 #include <media/v4l2-fwnode.h>
@@ -633,6 +636,11 @@ struct hi556 { · 620 unmodified lines
633636 struct v4l2_ctrl *hblank;
634637 struct v4l2_ctrl *exposure;
635638
639+ /* GPIOs, clocks, etc. */
640+ struct gpio_desc *reset_gpio;
641+ struct clk *clk;
642+ struct regulator *avdd;
643+
636644 /* Current mode */
637645 const struct hi556_mode *cur_mode;
638646
@@ -1206,8 +1214,18 @@ static int hi556_check_hwcfg(struct device *dev) · 567 unmodified lines
12061214 int ret = 0;
12071215 unsigned int i, j;
12081216
1209− if (!fwnode)
1210− return -ENXIO;
1217+ /*
1218+ * Sometimes the fwnode graph is initialized by the bridge driver,
1219+ * wait for this.
1220+ */
1221+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
1222+ if (!ep)
1223+ return -EPROBE_DEFER;
1224+
1225+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
1226+ fwnode_handle_put(ep);
1227+ if (ret)
1228+ return ret;
12111229
12121230 ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
12131231 if (ret) {
@@ -1220,15 +1238,6 @@ static int hi556_check_hwcfg(struct device *dev) · 6 unmodified lines
12201238 return -EINVAL;
12211239 }
12221240
1223− ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
1224− if (!ep)
1225− return -ENXIO;
1226−
1227− ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
1228− fwnode_handle_put(ep);
1229− if (ret)
1230− return ret;
1231−
12321241 if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
12331242 dev_err(dev, "number of CSI2 data lanes %d is not supported",
12341243 bus_cfg.bus.mipi_csi2.num_data_lanes);
@@ -1275,6 +1284,47 @@ static void hi556_remove(struct i2c_client *client) · 40 unmodified lines
12751284 mutex_destroy(&hi556->mutex);
12761285 }
12771286
1287+static int hi556_suspend(struct device *dev)
1288+{
1289+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
1290+ struct hi556 *hi556 = to_hi556(sd);
1291+ int ret;
1292+
1293+ gpiod_set_value_cansleep(hi556->reset_gpio, 1);
1294+
1295+ ret = regulator_disable(hi556->avdd);
1296+ if (ret) {
1297+ dev_err(dev, "failed to disable avdd: %d\n", ret);
1298+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
1299+ return ret;
1300+ }
1301+
1302+ clk_disable_unprepare(hi556->clk);
1303+ return 0;
1304+}
1305+
1306+static int hi556_resume(struct device *dev)
1307+{
1308+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
1309+ struct hi556 *hi556 = to_hi556(sd);
1310+ int ret;
1311+
1312+ ret = clk_prepare_enable(hi556->clk);
1313+ if (ret)
1314+ return ret;
1315+
1316+ ret = regulator_enable(hi556->avdd);
1317+ if (ret) {
1318+ dev_err(dev, "failed to enable avdd: %d\n", ret);
1319+ clk_disable_unprepare(hi556->clk);
1320+ return ret;
1321+ }
1322+
1323+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
1324+ usleep_range(5000, 5500);
1325+ return 0;
1326+}
1327+
12781328 static int hi556_probe(struct i2c_client *client)
12791329 {
12801330 struct hi556 *hi556;
@@ -1294,12 +1344,35 @@ static int hi556_probe(struct i2c_client *client) · 13 unmodified lines
12941344
12951345 v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops);
12961346
1347+ hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
1348+ GPIOD_OUT_HIGH);
1349+ if (IS_ERR(hi556->reset_gpio))
1350+ return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
1351+ "failed to get reset GPIO\n");
1352+
1353+ hi556->clk = devm_clk_get_optional(&client->dev, "clk");
1354+ if (IS_ERR(hi556->clk))
1355+ return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
1356+ "failed to get clock\n");
1357+
1358+ /* The regulator core will provide a "dummy" regulator if necessary */
1359+ hi556->avdd = devm_regulator_get(&client->dev, "avdd");
1360+ if (IS_ERR(hi556->avdd))
1361+ return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
1362+ "failed to get avdd regulator\n");
1363+
12971364 full_power = acpi_dev_state_d0(&client->dev);
12981365 if (full_power) {
1366+ /* Ensure non ACPI managed resources are enabled */
1367+ ret = hi556_resume(&client->dev);
1368+ if (ret)
1369+ return dev_err_probe(&client->dev, ret,
1370+ "failed to power on sensor\n");
1371+
12991372 ret = hi556_identify_module(hi556);
13001373 if (ret) {
13011374 dev_err(&client->dev, "failed to find sensor: %d", ret);
1302− return ret;
1375+ goto probe_error_power_off;
13031376 }
13041377 }
13051378
@@ -1344,9 +1417,16 @@ probe_error_v4l2_ctrl_handler_free: · 38 unmodified lines
13441417 v4l2_ctrl_handler_free(hi556->sd.ctrl_handler);
13451418 mutex_destroy(&hi556->mutex);
13461419
1420+probe_error_power_off:
1421+ if (full_power)
1422+ hi556_suspend(&client->dev);
1423+
13471424 return ret;
13481425 }
13491426
1427+static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume,
1428+ NULL);
1429+
13501430 #ifdef CONFIG_ACPI
13511431 static const struct acpi_device_id hi556_acpi_ids[] = {
13521432 {"INT3537"},
@@ -1360,6 +1440,7 @@ static struct i2c_driver hi556_i2c_driver = { · 7 unmodified lines
13601440 .driver = {
13611441 .name = "hi556",
13621442 .acpi_match_table = ACPI_PTR(hi556_acpi_ids),
1443+ .pm = pm_sleep_ptr(&hi556_pm_ops),
13631444 },
13641445 .probe = hi556_probe,
13651446 .remove = hi556_remove,
drivers/media/i2c/imx214.c+1−0
@@ -1114,6 +1114,7 @@ free_ctrl: · 1113 unmodified lines
11141114 v4l2_ctrl_handler_free(&imx214->ctrls);
11151115 error_power_off:
11161116 pm_runtime_disable(imx214->dev);
1117+ regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies);
11171118
11181119 return ret;
11191120 }
drivers/media/i2c/imx219.c+35−42
@@ -551,8 +551,7 @@ static int imx219_init_controls(struct imx219 *imx219) · 550 unmodified lines
551551
552552 if (ctrl_hdlr->error) {
553553 ret = ctrl_hdlr->error;
554− dev_err(&client->dev, "%s control init failed (%d)\n",
555− __func__, ret);
554+ dev_err_probe(&client->dev, ret, "Control init failed\n");
556555 goto error;
557556 }
558557
@@ -1024,17 +1023,15 @@ static int imx219_identify_module(struct imx219 *imx219) · 465 unmodified lines
10241023 u64 val;
10251024
10261025 ret = cci_read(imx219->regmap, IMX219_REG_CHIP_ID, &val, NULL);
1027− if (ret) {
1028− dev_err(&client->dev, "failed to read chip id %x\n",
1029− IMX219_CHIP_ID);
1030− return ret;
1031− }
1026+ if (ret)
1027+ return dev_err_probe(&client->dev, ret,
1028+ "failed to read chip id %x\n",
1029+ IMX219_CHIP_ID);
10321030
1033− if (val != IMX219_CHIP_ID) {
1034− dev_err(&client->dev, "chip id mismatch: %x!=%llx\n",
1035− IMX219_CHIP_ID, val);
1036− return -EIO;
1037− }
1031+ if (val != IMX219_CHIP_ID)
1032+ return dev_err_probe(&client->dev, -EIO,
1033+ "chip id mismatch: %x!=%llx\n",
1034+ IMX219_CHIP_ID, val);
10381035
10391036 return 0;
10401037 }
@@ -1048,35 +1045,36 @@ static int imx219_check_hwcfg(struct device *dev, struct imx219 *imx219) · 7 unmodified lines
10481045 int ret = -EINVAL;
10491046
10501047 endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
1051− if (!endpoint) {
1052− dev_err(dev, "endpoint node not found\n");
1053− return -EINVAL;
1054− }
1048+ if (!endpoint)
1049+ return dev_err_probe(dev, -EINVAL, "endpoint node not found\n");
10551050
10561051 if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
1057− dev_err(dev, "could not parse endpoint\n");
1052+ dev_err_probe(dev, -EINVAL, "could not parse endpoint\n");
10581053 goto error_out;
10591054 }
10601055
10611056 /* Check the number of MIPI CSI2 data lanes */
10621057 if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2 &&
10631058 ep_cfg.bus.mipi_csi2.num_data_lanes != 4) {
1064− dev_err(dev, "only 2 or 4 data lanes are currently supported\n");
1059+ dev_err_probe(dev, -EINVAL,
1060+ "only 2 or 4 data lanes are currently supported\n");
10651061 goto error_out;
10661062 }
10671063 imx219->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
10681064
10691065 /* Check the link frequency set in device tree */
10701066 if (!ep_cfg.nr_of_link_frequencies) {
1071− dev_err(dev, "link-frequency property not found in DT\n");
1067+ dev_err_probe(dev, -EINVAL,
1068+ "link-frequency property not found in DT\n");
10721069 goto error_out;
10731070 }
10741071
10751072 if (ep_cfg.nr_of_link_frequencies != 1 ||
10761073 (ep_cfg.link_frequencies[0] != ((imx219->lanes == 2) ?
10771074 IMX219_DEFAULT_LINK_FREQ : IMX219_DEFAULT_LINK_FREQ_4LANE))) {
1078− dev_err(dev, "Link frequency not supported: %lld\n",
1079− ep_cfg.link_frequencies[0]);
1075+ dev_err_probe(dev, -EINVAL,
1076+ "Link frequency not supported: %lld\n",
1077+ ep_cfg.link_frequencies[0]);
10801078 goto error_out;
10811079 }
10821080
@@ -1107,31 +1105,25 @@ static int imx219_probe(struct i2c_client *client) · 24 unmodified lines
11071105 return -EINVAL;
11081106
11091107 imx219->regmap = devm_cci_regmap_init_i2c(client, 16);
1110− if (IS_ERR(imx219->regmap)) {
1111− ret = PTR_ERR(imx219->regmap);
1112− dev_err(dev, "failed to initialize CCI: %d\n", ret);
1113− return ret;
1114− }
1108+ if (IS_ERR(imx219->regmap))
1109+ return dev_err_probe(dev, PTR_ERR(imx219->regmap),
1110+ "failed to initialize CCI\n");
11151111
11161112 /* Get system clock (xclk) */
11171113 imx219->xclk = devm_clk_get(dev, NULL);
1118− if (IS_ERR(imx219->xclk)) {
1119− dev_err(dev, "failed to get xclk\n");
1120− return PTR_ERR(imx219->xclk);
1121− }
1114+ if (IS_ERR(imx219->xclk))
1115+ return dev_err_probe(dev, PTR_ERR(imx219->xclk),
1116+ "failed to get xclk\n");
11221117
11231118 imx219->xclk_freq = clk_get_rate(imx219->xclk);
1124− if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
1125− dev_err(dev, "xclk frequency not supported: %d Hz\n",
1126− imx219->xclk_freq);
1127− return -EINVAL;
1128− }
1119+ if (imx219->xclk_freq != IMX219_XCLK_FREQ)
1120+ return dev_err_probe(dev, -EINVAL,
1121+ "xclk frequency not supported: %d Hz\n",
1122+ imx219->xclk_freq);
11291123
11301124 ret = imx219_get_regulators(imx219);
1131− if (ret) {
1132− dev_err(dev, "failed to get regulators\n");
1133− return ret;
1134− }
1125+ if (ret)
1126+ return dev_err_probe(dev, ret, "failed to get regulators\n");
11351127
11361128 /* Request optional enable pin */
11371129 imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
@@ -1183,20 +1175,21 @@ static int imx219_probe(struct i2c_client *client) · 45 unmodified lines
11831175
11841176 ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
11851177 if (ret) {
1186− dev_err(dev, "failed to init entity pads: %d\n", ret);
1178+ dev_err_probe(dev, ret, "failed to init entity pads\n");
11871179 goto error_handler_free;
11881180 }
11891181
11901182 imx219->sd.state_lock = imx219->ctrl_handler.lock;
11911183 ret = v4l2_subdev_init_finalize(&imx219->sd);
11921184 if (ret < 0) {
1193− dev_err(dev, "subdev init error: %d\n", ret);
1185+ dev_err_probe(dev, ret, "subdev init error\n");
11941186 goto error_media_entity;
11951187 }
11961188
11971189 ret = v4l2_async_register_subdev_sensor(&imx219->sd);
11981190 if (ret < 0) {
1199− dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
1191+ dev_err_probe(dev, ret,
1192+ "failed to register sensor sub-device\n");
12001193 goto error_subdev_cleanup;
12011194 }
12021195
drivers/media/i2c/imx335.c+304−333
@@ -11,57 +11,108 @@ · 10 unmodified lines
1111 #include <linux/i2c.h>
1212 #include <linux/module.h>
1313 #include <linux/pm_runtime.h>
14+#include <linux/regmap.h>
1415
16+#include <media/v4l2-cci.h>
1517 #include <media/v4l2-ctrls.h>
1618 #include <media/v4l2-fwnode.h>
1719 #include <media/v4l2-subdev.h>
1820
1921 /* Streaming Mode */
20−#define IMX335_REG_MODE_SELECT 0x3000
21−#define IMX335_MODE_STANDBY 0x01
22−#define IMX335_MODE_STREAMING 0x00
22+#define IMX335_REG_MODE_SELECT CCI_REG8(0x3000)
23+#define IMX335_MODE_STANDBY 0x01
24+#define IMX335_MODE_STREAMING 0x00
25+
26+/* Group hold register */
27+#define IMX335_REG_HOLD CCI_REG8(0x3001)
28+
29+#define IMX335_REG_MASTER_MODE CCI_REG8(0x3002)
30+#define IMX335_REG_BCWAIT_TIME CCI_REG8(0x300c)
31+#define IMX335_REG_CPWAIT_TIME CCI_REG8(0x300d)
32+#define IMX335_REG_WINMODE CCI_REG8(0x3018)
33+#define IMX335_REG_HTRIMMING_START CCI_REG16_LE(0x302c)
34+#define IMX335_REG_HNUM CCI_REG8(0x302e)
2335
2436 /* Lines per frame */
25−#define IMX335_REG_LPFR 0x3030
37+#define IMX335_REG_VMAX CCI_REG24_LE(0x3030)
2638
27−/* Chip ID */
28−#define IMX335_REG_ID 0x3912
29−#define IMX335_ID 0x00
30−
31−/* Exposure control */
32−#define IMX335_REG_SHUTTER 0x3058
33−#define IMX335_EXPOSURE_MIN 1
34−#define IMX335_EXPOSURE_OFFSET 9
35−#define IMX335_EXPOSURE_STEP 1
36−#define IMX335_EXPOSURE_DEFAULT 0x0648
37−
38−/* Analog gain control */
39−#define IMX335_REG_AGAIN 0x30e8
40−#define IMX335_AGAIN_MIN 0
41−#define IMX335_AGAIN_MAX 240
42−#define IMX335_AGAIN_STEP 1
43−#define IMX335_AGAIN_DEFAULT 0
39+#define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c)
40+#define IMX335_REG_ADBIT CCI_REG8(0x3050)
41+#define IMX335_REG_Y_OUT_SIZE CCI_REG16_LE(0x3056)
4442
45−/* Group hold register */
46−#define IMX335_REG_HOLD 0x3001
43+#define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058)
44+#define IMX335_EXPOSURE_MIN 1
45+#define IMX335_EXPOSURE_OFFSET 9
46+#define IMX335_EXPOSURE_STEP 1
47+#define IMX335_EXPOSURE_DEFAULT 0x0648
48+
49+#define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074)
50+#define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076)
51+
52+/* Analog and Digital gain control */
53+#define IMX335_REG_GAIN CCI_REG8(0x30e8)
54+#define IMX335_AGAIN_MIN 0
55+#define IMX335_AGAIN_MAX 100
56+#define IMX335_AGAIN_STEP 1
57+#define IMX335_AGAIN_DEFAULT 0
58+
59+#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148)
60+
61+#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c)
62+#define IMX335_REG_INCLKSEL2 CCI_REG8(0x315a)
63+#define IMX335_REG_INCLKSEL3 CCI_REG8(0x3168)
64+#define IMX335_REG_INCLKSEL4 CCI_REG8(0x316a)
65+
66+#define IMX335_REG_MDBIT CCI_REG8(0x319d)
67+#define IMX335_REG_SYSMODE CCI_REG8(0x319e)
68+
69+#define IMX335_REG_XVS_XHS_DRV CCI_REG8(0x31a1)
4770
4871 /* Test pattern generator */
49−#define IMX335_REG_TPG 0x329e
50−#define IMX335_TPG_ALL_000 0
51−#define IMX335_TPG_ALL_FFF 1
52−#define IMX335_TPG_ALL_555 2
53−#define IMX335_TPG_ALL_AAA 3
54−#define IMX335_TPG_TOG_555_AAA 4
55−#define IMX335_TPG_TOG_AAA_555 5
56−#define IMX335_TPG_TOG_000_555 6
57−#define IMX335_TPG_TOG_555_000 7
58−#define IMX335_TPG_TOG_000_FFF 8
59−#define IMX335_TPG_TOG_FFF_000 9
60−#define IMX335_TPG_H_COLOR_BARS 10
61−#define IMX335_TPG_V_COLOR_BARS 11
72+#define IMX335_REG_TPG_DIG_CLP_MODE CCI_REG8(0x3280)
73+#define IMX335_REG_TPG_EN_DUOUT CCI_REG8(0x329c)
74+#define IMX335_REG_TPG CCI_REG8(0x329e)
75+#define IMX335_TPG_ALL_000 0
76+#define IMX335_TPG_ALL_FFF 1
77+#define IMX335_TPG_ALL_555 2
78+#define IMX335_TPG_ALL_AAA 3
79+#define IMX335_TPG_TOG_555_AAA 4
80+#define IMX335_TPG_TOG_AAA_555 5
81+#define IMX335_TPG_TOG_000_555 6
82+#define IMX335_TPG_TOG_555_000 7
83+#define IMX335_TPG_TOG_000_FFF 8
84+#define IMX335_TPG_TOG_FFF_000 9
85+#define IMX335_TPG_H_COLOR_BARS 10
86+#define IMX335_TPG_V_COLOR_BARS 11
87+#define IMX335_REG_TPG_COLORWIDTH CCI_REG8(0x32a0)
88+
89+#define IMX335_REG_BLKLEVEL CCI_REG16_LE(0x3302)
90+
91+#define IMX335_REG_WRJ_OPEN CCI_REG8(0x336c)
92+
93+#define IMX335_REG_ADBIT1 CCI_REG16_LE(0x341c)
94+
95+/* Chip ID */
96+#define IMX335_REG_ID CCI_REG8(0x3912)
97+#define IMX335_ID 0x00
98+
99+/* Data Lanes */
100+#define IMX335_REG_LANEMODE CCI_REG8(0x3a01)
101+#define IMX335_2LANE 1
102+#define IMX335_4LANE 3
103+
104+#define IMX335_REG_TCLKPOST CCI_REG16_LE(0x3a18)
105+#define IMX335_REG_TCLKPREPARE CCI_REG16_LE(0x3a1a)
106+#define IMX335_REG_TCLK_TRAIL CCI_REG16_LE(0x3a1c)
107+#define IMX335_REG_TCLK_ZERO CCI_REG16_LE(0x3a1e)
108+#define IMX335_REG_THS_PREPARE CCI_REG16_LE(0x3a20)
109+#define IMX335_REG_THS_ZERO CCI_REG16_LE(0x3a22)
110+#define IMX335_REG_THS_TRAIL CCI_REG16_LE(0x3a24)
111+#define IMX335_REG_THS_EXIT CCI_REG16_LE(0x3a26)
112+#define IMX335_REG_TPLX CCI_REG16_LE(0x3a28)
62113
63114 /* Input clock rate */
64−#define IMX335_INCLK_RATE 24000000
115+#define IMX335_INCLK_RATE 24000000
65116
66117 /* CSI2 HW configuration */
67118 #define IMX335_LINK_FREQ_594MHz 594000000LL
@@ -69,9 +120,6 @@ · 1 unmodified line
69120
70121 #define IMX335_NUM_DATA_LANES 4
71122
72−#define IMX335_REG_MIN 0x00
73−#define IMX335_REG_MAX 0xfffff
74−
75123 /* IMX335 native and active pixel array size. */
76124 #define IMX335_NATIVE_WIDTH 2616U
77125 #define IMX335_NATIVE_HEIGHT 1964U
@@ -80,16 +128,6 @@ · 2 unmodified lines
80128 #define IMX335_PIXEL_ARRAY_WIDTH 2592U
81129 #define IMX335_PIXEL_ARRAY_HEIGHT 1944U
82130
83−/**
84− * struct imx335_reg - imx335 sensor register
85− * @address: Register address
86− * @val: Register value
87− */
88−struct imx335_reg {
89− u16 address;
90− u8 val;
91−};
92−
93131 /**
94132 * struct imx335_reg_list - imx335 sensor register list
95133 * @num_of_regs: Number of registers in the list
@@ -97,7 +135,7 @@ struct imx335_reg { · 1 unmodified line
97135 */
98136 struct imx335_reg_list {
99137 u32 num_of_regs;
100− const struct imx335_reg *regs;
138+ const struct cci_reg_sequence *regs;
101139 };
102140
103141 static const char * const imx335_supply_name[] = {
@@ -138,6 +176,7 @@ struct imx335_mode { · 34 unmodified lines
138176 * @pad: Media pad. Only one pad supported
139177 * @reset_gpio: Sensor reset gpio
140178 * @supplies: Regulator supplies to handle power control
179+ * @cci: CCI register map
141180 * @inclk: Sensor input clock
142181 * @ctrl_handler: V4L2 control handler
143182 * @link_freq_ctrl: Pointer to link frequency control
@@ -147,6 +186,7 @@ struct imx335_mode { · 3 unmodified lines
147186 * @exp_ctrl: Pointer to exposure control
148187 * @again_ctrl: Pointer to analog gain control
149188 * @vblank: Vertical blanking in lines
189+ * @lane_mode: Mode for number of connected data lanes
150190 * @cur_mode: Pointer to current selected sensor mode
151191 * @mutex: Mutex for serializing sensor controls
152192 * @link_freq_bitmap: Menu bitmap for link_freq_ctrl
@@ -159,6 +199,7 @@ struct imx335 { · 6 unmodified lines
159199 struct media_pad pad;
160200 struct gpio_desc *reset_gpio;
161201 struct regulator_bulk_data supplies[ARRAY_SIZE(imx335_supply_name)];
202+ struct regmap *cci;
162203
163204 struct clk *inclk;
164205 struct v4l2_ctrl_handler ctrl_handler;
@@ -171,6 +212,7 @@ struct imx335 { · 6 unmodified lines
171212 struct v4l2_ctrl *again_ctrl;
172213 };
173214 u32 vblank;
215+ u32 lane_mode;
174216 const struct imx335_mode *cur_mode;
175217 struct mutex mutex;
176218 unsigned long link_freq_bitmap;
@@ -210,140 +252,135 @@ static const int imx335_tpg_val[] = { · 33 unmodified lines
210252 };
211253
212254 /* Sensor mode registers */
213−static const struct imx335_reg mode_2592x1940_regs[] = {
214− {0x3000, 0x01},
215− {0x3002, 0x00},
216− {0x3018, 0x04},
217− {0x302c, 0x3c},
218− {0x302e, 0x20},
219− {0x3056, 0x94},
220− {0x3074, 0xc8},
221− {0x3076, 0x28},
222− {0x304c, 0x00},
223− {0x31a1, 0x00},
224− {0x3288, 0x21},
225− {0x328a, 0x02},
226− {0x3414, 0x05},
227− {0x3416, 0x18},
228− {0x3648, 0x01},
229− {0x364a, 0x04},
230− {0x364c, 0x04},
231− {0x3678, 0x01},
232− {0x367c, 0x31},
233− {0x367e, 0x31},
234− {0x3706, 0x10},
235− {0x3708, 0x03},
236− {0x3714, 0x02},
237− {0x3715, 0x02},
238− {0x3716, 0x01},
239− {0x3717, 0x03},
240− {0x371c, 0x3d},
241− {0x371d, 0x3f},
242− {0x372c, 0x00},
243− {0x372d, 0x00},
244− {0x372e, 0x46},
245− {0x372f, 0x00},
246− {0x3730, 0x89},
247− {0x3731, 0x00},
248− {0x3732, 0x08},
249− {0x3733, 0x01},
250− {0x3734, 0xfe},
251− {0x3735, 0x05},
252− {0x3740, 0x02},
253− {0x375d, 0x00},
254− {0x375e, 0x00},
255− {0x375f, 0x11},
256− {0x3760, 0x01},
257− {0x3768, 0x1b},
258− {0x3769, 0x1b},
259− {0x376a, 0x1b},
260− {0x376b, 0x1b},
261− {0x376c, 0x1a},
262− {0x376d, 0x17},
263− {0x376e, 0x0f},
264− {0x3776, 0x00},
265− {0x3777, 0x00},
266− {0x3778, 0x46},
267− {0x3779, 0x00},
268− {0x377a, 0x89},
269− {0x377b, 0x00},
270− {0x377c, 0x08},
271− {0x377d, 0x01},
272− {0x377e, 0x23},
273− {0x377f, 0x02},
274− {0x3780, 0xd9},
275− {0x3781, 0x03},
276− {0x3782, 0xf5},
277− {0x3783, 0x06},
278− {0x3784, 0xa5},
279− {0x3788, 0x0f},
280− {0x378a, 0xd9},
281− {0x378b, 0x03},
282− {0x378c, 0xeb},
283− {0x378d, 0x05},
284− {0x378e, 0x87},
285− {0x378f, 0x06},
286− {0x3790, 0xf5},
287− {0x3792, 0x43},
288− {0x3794, 0x7a},
289− {0x3796, 0xa1},
290− {0x37b0, 0x36},
291− {0x3a00, 0x00},
255+static const struct cci_reg_sequence mode_2592x1940_regs[] = {
256+ { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
257+ { IMX335_REG_MASTER_MODE, 0x00 },
258+ { IMX335_REG_WINMODE, 0x04 },
259+ { IMX335_REG_HTRIMMING_START, 48 },
260+ { IMX335_REG_HNUM, 2592 },
261+ { IMX335_REG_Y_OUT_SIZE, 1944 },
262+ { IMX335_REG_AREA3_ST_ADR_1, 176 },
263+ { IMX335_REG_AREA3_WIDTH_1, 3928 },
264+ { IMX335_REG_OPB_SIZE_V, 0 },
265+ { IMX335_REG_XVS_XHS_DRV, 0x00 },
266+ { CCI_REG8(0x3288), 0x21 },
267+ { CCI_REG8(0x328a), 0x02 },
268+ { CCI_REG8(0x3414), 0x05 },
269+ { CCI_REG8(0x3416), 0x18 },
270+ { CCI_REG8(0x3648), 0x01 },
271+ { CCI_REG8(0x364a), 0x04 },
272+ { CCI_REG8(0x364c), 0x04 },
273+ { CCI_REG8(0x3678), 0x01 },
274+ { CCI_REG8(0x367c), 0x31 },
275+ { CCI_REG8(0x367e), 0x31 },
276+ { CCI_REG8(0x3706), 0x10 },
277+ { CCI_REG8(0x3708), 0x03 },
278+ { CCI_REG8(0x3714), 0x02 },
279+ { CCI_REG8(0x3715), 0x02 },
280+ { CCI_REG8(0x3716), 0x01 },
281+ { CCI_REG8(0x3717), 0x03 },
282+ { CCI_REG8(0x371c), 0x3d },
283+ { CCI_REG8(0x371d), 0x3f },
284+ { CCI_REG8(0x372c), 0x00 },
285+ { CCI_REG8(0x372d), 0x00 },
286+ { CCI_REG8(0x372e), 0x46 },
287+ { CCI_REG8(0x372f), 0x00 },
288+ { CCI_REG8(0x3730), 0x89 },
289+ { CCI_REG8(0x3731), 0x00 },
290+ { CCI_REG8(0x3732), 0x08 },
291+ { CCI_REG8(0x3733), 0x01 },
292+ { CCI_REG8(0x3734), 0xfe },
293+ { CCI_REG8(0x3735), 0x05 },
294+ { CCI_REG8(0x3740), 0x02 },
295+ { CCI_REG8(0x375d), 0x00 },
296+ { CCI_REG8(0x375e), 0x00 },
297+ { CCI_REG8(0x375f), 0x11 },
298+ { CCI_REG8(0x3760), 0x01 },
299+ { CCI_REG8(0x3768), 0x1b },
300+ { CCI_REG8(0x3769), 0x1b },
301+ { CCI_REG8(0x376a), 0x1b },
302+ { CCI_REG8(0x376b), 0x1b },
303+ { CCI_REG8(0x376c), 0x1a },
304+ { CCI_REG8(0x376d), 0x17 },
305+ { CCI_REG8(0x376e), 0x0f },
306+ { CCI_REG8(0x3776), 0x00 },
307+ { CCI_REG8(0x3777), 0x00 },
308+ { CCI_REG8(0x3778), 0x46 },
309+ { CCI_REG8(0x3779), 0x00 },
310+ { CCI_REG8(0x377a), 0x89 },
311+ { CCI_REG8(0x377b), 0x00 },
312+ { CCI_REG8(0x377c), 0x08 },
313+ { CCI_REG8(0x377d), 0x01 },
314+ { CCI_REG8(0x377e), 0x23 },
315+ { CCI_REG8(0x377f), 0x02 },
316+ { CCI_REG8(0x3780), 0xd9 },
317+ { CCI_REG8(0x3781), 0x03 },
318+ { CCI_REG8(0x3782), 0xf5 },
319+ { CCI_REG8(0x3783), 0x06 },
320+ { CCI_REG8(0x3784), 0xa5 },
321+ { CCI_REG8(0x3788), 0x0f },
322+ { CCI_REG8(0x378a), 0xd9 },
323+ { CCI_REG8(0x378b), 0x03 },
324+ { CCI_REG8(0x378c), 0xeb },
325+ { CCI_REG8(0x378d), 0x05 },
326+ { CCI_REG8(0x378e), 0x87 },
327+ { CCI_REG8(0x378f), 0x06 },
328+ { CCI_REG8(0x3790), 0xf5 },
329+ { CCI_REG8(0x3792), 0x43 },
330+ { CCI_REG8(0x3794), 0x7a },
331+ { CCI_REG8(0x3796), 0xa1 },
332+ { CCI_REG8(0x37b0), 0x36 },
333+ { CCI_REG8(0x3a00), 0x00 },
292334 };
293335
294−static const struct imx335_reg raw10_framefmt_regs[] = {
295− {0x3050, 0x00},
296− {0x319d, 0x00},
297− {0x341c, 0xff},
298− {0x341d, 0x01},
336+static const struct cci_reg_sequence raw10_framefmt_regs[] = {
337+ { IMX335_REG_ADBIT, 0x00 },
338+ { IMX335_REG_MDBIT, 0x00 },
339+ { IMX335_REG_ADBIT1, 0x1ff },
299340 };
300341
301−static const struct imx335_reg raw12_framefmt_regs[] = {
302− {0x3050, 0x01},
303− {0x319d, 0x01},
304− {0x341c, 0x47},
305− {0x341d, 0x00},
342+static const struct cci_reg_sequence raw12_framefmt_regs[] = {
343+ { IMX335_REG_ADBIT, 0x01 },
344+ { IMX335_REG_MDBIT, 0x01 },
345+ { IMX335_REG_ADBIT1, 0x47 },
306346 };
307347
308−static const struct imx335_reg mipi_data_rate_1188Mbps[] = {
309− {0x300c, 0x3b},
310− {0x300d, 0x2a},
311− {0x314c, 0xc6},
312− {0x314d, 0x00},
313− {0x315a, 0x02},
314− {0x3168, 0xa0},
315− {0x316a, 0x7e},
316− {0x319e, 0x01},
317− {0x3a18, 0x8f},
318− {0x3a1a, 0x4f},
319− {0x3a1c, 0x47},
320− {0x3a1e, 0x37},
321− {0x3a1f, 0x01},
322− {0x3a20, 0x4f},
323− {0x3a22, 0x87},
324− {0x3a24, 0x4f},
325− {0x3a26, 0x7f},
326− {0x3a28, 0x3f},
348+static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = {
349+ { IMX335_REG_BCWAIT_TIME, 0x3b },
350+ { IMX335_REG_CPWAIT_TIME, 0x2a },
351+ { IMX335_REG_INCLKSEL1, 0x00c6 },
352+ { IMX335_REG_INCLKSEL2, 0x02 },
353+ { IMX335_REG_INCLKSEL3, 0xa0 },
354+ { IMX335_REG_INCLKSEL4, 0x7e },
355+ { IMX335_REG_SYSMODE, 0x01 },
356+ { IMX335_REG_TCLKPOST, 0x8f },
357+ { IMX335_REG_TCLKPREPARE, 0x4f },
358+ { IMX335_REG_TCLK_TRAIL, 0x47 },
359+ { IMX335_REG_TCLK_ZERO, 0x0137 },
360+ { IMX335_REG_THS_PREPARE, 0x4f },
361+ { IMX335_REG_THS_ZERO, 0x87 },
362+ { IMX335_REG_THS_TRAIL, 0x4f },
363+ { IMX335_REG_THS_EXIT, 0x7f },
364+ { IMX335_REG_TPLX, 0x3f },
327365 };
328366
329−static const struct imx335_reg mipi_data_rate_891Mbps[] = {
330− {0x300c, 0x3b},
331− {0x300d, 0x2a},
332− {0x314c, 0x29},
333− {0x314d, 0x01},
334− {0x315a, 0x06},
335− {0x3168, 0xa0},
336− {0x316a, 0x7e},
337− {0x319e, 0x02},
338− {0x3a18, 0x7f},
339− {0x3a1a, 0x37},
340− {0x3a1c, 0x37},
341− {0x3a1e, 0xf7},
342− {0x3a20, 0x3f},
343− {0x3a22, 0x6f},
344− {0x3a24, 0x3f},
345− {0x3a26, 0x5f},
346− {0x3a28, 0x2f},
367+static const struct cci_reg_sequence mipi_data_rate_891Mbps[] = {
368+ { IMX335_REG_BCWAIT_TIME, 0x3b },
369+ { IMX335_REG_CPWAIT_TIME, 0x2a },
370+ { IMX335_REG_INCLKSEL1, 0x0129 },
371+ { IMX335_REG_INCLKSEL2, 0x06 },
372+ { IMX335_REG_INCLKSEL3, 0xa0 },
373+ { IMX335_REG_INCLKSEL4, 0x7e },
374+ { IMX335_REG_SYSMODE, 0x02 },
375+ { IMX335_REG_TCLKPOST, 0x7f },
376+ { IMX335_REG_TCLKPREPARE, 0x37 },
377+ { IMX335_REG_TCLK_TRAIL, 0x37 },
378+ { IMX335_REG_TCLK_ZERO, 0xf7 },
379+ { IMX335_REG_THS_PREPARE, 0x3f },
380+ { IMX335_REG_THS_ZERO, 0x6f },
381+ { IMX335_REG_THS_TRAIL, 0x3f },
382+ { IMX335_REG_THS_EXIT, 0x5f },
383+ { IMX335_REG_TPLX, 0x2f },
347384 };
348385
349386 static const s64 link_freq[] = {
@@ -372,10 +409,10 @@ static const u32 imx335_mbus_codes[] = { · 22 unmodified lines
372409 /* Supported sensor mode configurations */
373410 static const struct imx335_mode supported_mode = {
374411 .width = 2592,
375− .height = 1940,
412+ .height = 1944,
376413 .hblank = 342,
377− .vblank = 2560,
378− .vblank_min = 2560,
414+ .vblank = 2556,
415+ .vblank_min = 2556,
379416 .vblank_max = 133060,
380417 .pclk = 396000000,
381418 .reg_list = {
@@ -395,101 +432,6 @@ static inline struct imx335 *to_imx335(struct v4l2_subdev *subdev) · 13 unmodified lines
395432 return container_of(subdev, struct imx335, sd);
396433 }
397434
398−/**
399− * imx335_read_reg() - Read registers.
400− * @imx335: pointer to imx335 device
401− * @reg: register address
402− * @len: length of bytes to read. Max supported bytes is 4
403− * @val: pointer to register value to be filled.
404− *
405− * Big endian register addresses with little endian values.
406− *
407− * Return: 0 if successful, error code otherwise.
408− */
409−static int imx335_read_reg(struct imx335 *imx335, u16 reg, u32 len, u32 *val)
410−{
411− struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
412− struct i2c_msg msgs[2] = {0};
413− u8 addr_buf[2] = {0};
414− u8 data_buf[4] = {0};
415− int ret;
416−
417− if (WARN_ON(len > 4))
418− return -EINVAL;
419−
420− put_unaligned_be16(reg, addr_buf);
421−
422− /* Write register address */
423− msgs[0].addr = client->addr;
424− msgs[0].flags = 0;
425− msgs[0].len = ARRAY_SIZE(addr_buf);
426− msgs[0].buf = addr_buf;
427−
428− /* Read data from register */
429− msgs[1].addr = client->addr;
430− msgs[1].flags = I2C_M_RD;
431− msgs[1].len = len;
432− msgs[1].buf = data_buf;
433−
434− ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
435− if (ret != ARRAY_SIZE(msgs))
436− return -EIO;
437−
438− *val = get_unaligned_le32(data_buf);
439−
440− return 0;
441−}
442−
443−/**
444− * imx335_write_reg() - Write register
445− * @imx335: pointer to imx335 device
446− * @reg: register address
447− * @len: length of bytes. Max supported bytes is 4
448− * @val: register value
449− *
450− * Big endian register addresses with little endian values.
451− *
452− * Return: 0 if successful, error code otherwise.
453− */
454−static int imx335_write_reg(struct imx335 *imx335, u16 reg, u32 len, u32 val)
455−{
456− struct i2c_client *client = v4l2_get_subdevdata(&imx335->sd);
457− u8 buf[6] = {0};
458−
459− if (WARN_ON(len > 4))
460− return -EINVAL;
461−
462− put_unaligned_be16(reg, buf);
463− put_unaligned_le32(val, buf + 2);
464− if (i2c_master_send(client, buf, len + 2) != len + 2)
465− return -EIO;
466−
467− return 0;
468−}
469−
470−/**
471− * imx335_write_regs() - Write a list of registers
472− * @imx335: pointer to imx335 device
473− * @regs: list of registers to be written
474− * @len: length of registers array
475− *
476− * Return: 0 if successful. error code otherwise.
477− */
478−static int imx335_write_regs(struct imx335 *imx335,
479− const struct imx335_reg *regs, u32 len)
480−{
481− unsigned int i;
482− int ret;
483−
484− for (i = 0; i < len; i++) {
485− ret = imx335_write_reg(imx335, regs[i].address, 1, regs[i].val);
486− if (ret)
487− return ret;
488− }
489−
490− return 0;
491−}
492−
493435 /**
494436 * imx335_update_controls() - Update control ranges based on streaming mode
495437 * @imx335: pointer to imx335 device
@@ -526,7 +468,8 @@ static int imx335_update_controls(struct imx335 *imx335, · 30 unmodified lines
526468 static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
527469 {
528470 u32 lpfr, shutter;
529− int ret;
471+ int ret_hold;
472+ int ret = 0;
530473
531474 lpfr = imx335->vblank + imx335->cur_mode->height;
532475 shutter = lpfr - exposure;
@@ -534,64 +477,55 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain) · 1 unmodified line
534477 dev_dbg(imx335->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u\n",
535478 exposure, gain, shutter, lpfr);
536479
537− ret = imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 1);
538− if (ret)
539− return ret;
540−
541− ret = imx335_write_reg(imx335, IMX335_REG_LPFR, 3, lpfr);
542− if (ret)
543− goto error_release_group_hold;
544−
545− ret = imx335_write_reg(imx335, IMX335_REG_SHUTTER, 3, shutter);
546− if (ret)
547− goto error_release_group_hold;
548−
549− ret = imx335_write_reg(imx335, IMX335_REG_AGAIN, 2, gain);
550−
551−error_release_group_hold:
552− imx335_write_reg(imx335, IMX335_REG_HOLD, 1, 0);
480+ cci_write(imx335->cci, IMX335_REG_HOLD, 1, &ret);
481+ cci_write(imx335->cci, IMX335_REG_VMAX, lpfr, &ret);
482+ cci_write(imx335->cci, IMX335_REG_SHUTTER, shutter, &ret);
483+ cci_write(imx335->cci, IMX335_REG_GAIN, gain, &ret);
484+ /*
485+ * Unconditionally attempt to release the hold, but track the
486+ * error if the unhold itself fails.
487+ */
488+ ret_hold = cci_write(imx335->cci, IMX335_REG_HOLD, 0, NULL);
489+ if (ret_hold)
490+ ret = ret_hold;
553491
554492 return ret;
555493 }
556494
557495 static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
558496 {
559− int ret;
497+ int ret = 0;
560498
561499 if (pattern_index >= ARRAY_SIZE(imx335_tpg_val))
562500 return -EINVAL;
563501
564502 if (pattern_index) {
565− const struct imx335_reg tpg_enable_regs[] = {
566− { 0x3148, 0x10 },
567− { 0x3280, 0x00 },
568− { 0x329c, 0x01 },
569− { 0x32a0, 0x11 },
570− { 0x3302, 0x00 },
571− { 0x3303, 0x00 },
572− { 0x336c, 0x00 },
503+ const struct cci_reg_sequence tpg_enable_regs[] = {
504+ { IMX335_REG_TPG_TESTCLKEN, 0x10 },
505+ { IMX335_REG_TPG_DIG_CLP_MODE, 0x00 },
506+ { IMX335_REG_TPG_EN_DUOUT, 0x01 },
507+ { IMX335_REG_TPG_COLORWIDTH, 0x11 },
508+ { IMX335_REG_BLKLEVEL, 0x00 },
509+ { IMX335_REG_WRJ_OPEN, 0x00 },
573510 };
574511
575− ret = imx335_write_reg(imx335, IMX335_REG_TPG, 1,
576− imx335_tpg_val[pattern_index]);
577− if (ret)
578− return ret;
512+ cci_write(imx335->cci, IMX335_REG_TPG,
513+ imx335_tpg_val[pattern_index], &ret);
579514
580− ret = imx335_write_regs(imx335, tpg_enable_regs,
581− ARRAY_SIZE(tpg_enable_regs));
515+ cci_multi_reg_write(imx335->cci, tpg_enable_regs,
516+ ARRAY_SIZE(tpg_enable_regs), &ret);
582517 } else {
583− const struct imx335_reg tpg_disable_regs[] = {
584− { 0x3148, 0x00 },
585− { 0x3280, 0x01 },
586− { 0x329c, 0x00 },
587− { 0x32a0, 0x10 },
588− { 0x3302, 0x32 },
589− { 0x3303, 0x00 },
590− { 0x336c, 0x01 },
518+ const struct cci_reg_sequence tpg_disable_regs[] = {
519+ { IMX335_REG_TPG_TESTCLKEN, 0x00 },
520+ { IMX335_REG_TPG_DIG_CLP_MODE, 0x01 },
521+ { IMX335_REG_TPG_EN_DUOUT, 0x00 },
522+ { IMX335_REG_TPG_COLORWIDTH, 0x10 },
523+ { IMX335_REG_BLKLEVEL, 0x32 },
524+ { IMX335_REG_WRJ_OPEN, 0x01 },
591525 };
592526
593− ret = imx335_write_regs(imx335, tpg_disable_regs,
594− ARRAY_SIZE(tpg_disable_regs));
527+ cci_multi_reg_write(imx335->cci, tpg_disable_regs,
528+ ARRAY_SIZE(tpg_disable_regs), &ret);
595529 }
596530
597531 return ret;
@@ -890,12 +824,14 @@ static int imx335_set_framefmt(struct imx335 *imx335) · 292 unmodified lines
890824 {
891825 switch (imx335->cur_mbus_code) {
892826 case MEDIA_BUS_FMT_SRGGB10_1X10:
893− return imx335_write_regs(imx335, raw10_framefmt_regs,
894− ARRAY_SIZE(raw10_framefmt_regs));
827+ return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs,
828+ ARRAY_SIZE(raw10_framefmt_regs),
829+ NULL);
895830
896831 case MEDIA_BUS_FMT_SRGGB12_1X12:
897− return imx335_write_regs(imx335, raw12_framefmt_regs,
898− ARRAY_SIZE(raw12_framefmt_regs));
832+ return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs,
833+ ARRAY_SIZE(raw12_framefmt_regs),
834+ NULL);
899835 }
900836
901837 return -EINVAL;
@@ -914,7 +850,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 12 unmodified lines
914850
915851 /* Setup PLL */
916852 reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)];
917− ret = imx335_write_regs(imx335, reg_list->regs, reg_list->num_of_regs);
853+ ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
854+ reg_list->num_of_regs, NULL);
918855 if (ret) {
919856 dev_err(imx335->dev, "%s failed to set plls\n", __func__);
920857 return ret;
@@ -922,8 +859,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 1 unmodified line
922859
923860 /* Write sensor mode registers */
924861 reg_list = &imx335->cur_mode->reg_list;
925− ret = imx335_write_regs(imx335, reg_list->regs,
926− reg_list->num_of_regs);
862+ ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
863+ reg_list->num_of_regs, NULL);
927864 if (ret) {
928865 dev_err(imx335->dev, "fail to write initial registers\n");
929866 return ret;
@@ -936,6 +873,12 @@ static int imx335_start_streaming(struct imx335 *imx335) · 6 unmodified lines
936873 return ret;
937874 }
938875
876+ /* Configure lanes */
877+ ret = cci_write(imx335->cci, IMX335_REG_LANEMODE,
878+ imx335->lane_mode, NULL);
879+ if (ret)
880+ return ret;
881+
939882 /* Setup handler will write actual exposure and gain */
940883 ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler);
941884 if (ret) {
@@ -944,8 +887,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 2 unmodified lines
944887 }
945888
946889 /* Start streaming */
947− ret = imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
948− 1, IMX335_MODE_STREAMING);
890+ ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
891+ IMX335_MODE_STREAMING, NULL);
949892 if (ret) {
950893 dev_err(imx335->dev, "fail to start streaming\n");
951894 return ret;
@@ -965,8 +908,8 @@ static int imx335_start_streaming(struct imx335 *imx335) · 13 unmodified lines
965908 */
966909 static int imx335_stop_streaming(struct imx335 *imx335)
967910 {
968− return imx335_write_reg(imx335, IMX335_REG_MODE_SELECT,
969− 1, IMX335_MODE_STANDBY);
911+ return cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
912+ IMX335_MODE_STANDBY, NULL);
970913 }
971914
972915 /**
@@ -1017,14 +960,14 @@ error_unlock: · 44 unmodified lines
1017960 static int imx335_detect(struct imx335 *imx335)
1018961 {
1019962 int ret;
1020− u32 val;
963+ u64 val;
1021964
1022− ret = imx335_read_reg(imx335, IMX335_REG_ID, 2, &val);
965+ ret = cci_read(imx335->cci, IMX335_REG_ID, &val, NULL);
1023966 if (ret)
1024967 return ret;
1025968
1026969 if (val != IMX335_ID) {
1027− dev_err(imx335->dev, "chip id mismatch: %x!=%x\n",
970+ dev_err(imx335->dev, "chip id mismatch: %x!=%llx\n",
1028971 IMX335_ID, val);
1029972 return -ENXIO;
1030973 }
@@ -1096,7 +1039,14 @@ static int imx335_parse_hw_config(struct imx335 *imx335) · 65 unmodified lines
10961039 if (ret)
10971040 return ret;
10981041
1099− if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX335_NUM_DATA_LANES) {
1042+ switch (bus_cfg.bus.mipi_csi2.num_data_lanes) {
1043+ case 2:
1044+ imx335->lane_mode = IMX335_2LANE;
1045+ break;
1046+ case 4:
1047+ imx335->lane_mode = IMX335_4LANE;
1048+ break;
1049+ default:
11001050 dev_err(imx335->dev,
11011051 "number of CSI2 data lanes %d is not supported\n",
11021052 bus_cfg.bus.mipi_csi2.num_data_lanes);
@@ -1208,10 +1158,16 @@ static int imx335_init_controls(struct imx335 *imx335) · 105 unmodified lines
12081158 {
12091159 struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler;
12101160 const struct imx335_mode *mode = imx335->cur_mode;
1161+ struct v4l2_fwnode_device_properties props;
12111162 u32 lpfr;
12121163 int ret;
12131164
1214− ret = v4l2_ctrl_handler_init(ctrl_hdlr, 7);
1165+ ret = v4l2_fwnode_device_parse(imx335->dev, &props);
1166+ if (ret)
1167+ return ret;
1168+
1169+ /* v4l2_fwnode_device_properties can add two more controls */
1170+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
12151171 if (ret)
12161172 return ret;
12171173
@@ -1228,6 +1184,14 @@ static int imx335_init_controls(struct imx335 *imx335) · 10 unmodified lines
12281184 IMX335_EXPOSURE_STEP,
12291185 IMX335_EXPOSURE_DEFAULT);
12301186
1187+ /*
1188+ * The sensor has an analog gain and a digital gain, both controlled
1189+ * through a single gain value, expressed in 0.3dB increments. Values
1190+ * from 0.0dB (0) to 30.0dB (100) apply analog gain only, higher values
1191+ * up to 72.0dB (240) add further digital gain. Limit the range to
1192+ * analog gain only, support for digital gain can be added separately
1193+ * if needed.
1194+ */
12311195 imx335->again_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
12321196 &imx335_ctrl_ops,
12331197 V4L2_CID_ANALOGUE_GAIN,
@@ -1276,6 +1240,8 @@ static int imx335_init_controls(struct imx335 *imx335) · 42 unmodified lines
12761240 if (imx335->hblank_ctrl)
12771241 imx335->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
12781242
1243+ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx335_ctrl_ops, &props);
1244+
12791245 if (ctrl_hdlr->error) {
12801246 dev_err(imx335->dev, "control init failed: %d\n",
12811247 ctrl_hdlr->error);
@@ -1304,6 +1270,11 @@ static int imx335_probe(struct i2c_client *client) · 22 unmodified lines
13041270 return -ENOMEM;
13051271
13061272 imx335->dev = &client->dev;
1273+ imx335->cci = devm_cci_regmap_init_i2c(client, 16);
1274+ if (IS_ERR(imx335->cci)) {
1275+ dev_err(imx335->dev, "Unable to initialize I2C\n");
1276+ return -ENODEV;
1277+ }
13071278
13081279 /* Initialize subdev */
13091280 v4l2_i2c_subdev_init(&imx335->sd, client, &imx335_subdev_ops);
drivers/media/i2c/max9271.h+5−0
@@ -8,6 +8,9 @@ · 7 unmodified lines
88 * Copyright (C) 2015 Cogent Embedded, Inc.
99 */
1010
11+#ifndef __MEDIA_I2C_MAX9271_H__
12+#define __MEDIA_I2C_MAX9271_H__
13+
1114 #include <linux/i2c.h>
1215
1316 #define MAX9271_DEFAULT_ADDR 0x40
@@ -231,3 +234,5 @@ int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr); · 217 unmodified lines
231234 * Return 0 on success or a negative error code on failure
232235 */
233236 int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
237+
238+#endif /* __MEDIA_I2C_MAX9271_H__ */
drivers/media/i2c/ov2680.c+77−26
@@ -75,6 +75,8 @@ · 74 unmodified lines
7575 #define OV2680_ACTIVE_START_TOP 8
7676 #define OV2680_MIN_CROP_WIDTH 2
7777 #define OV2680_MIN_CROP_HEIGHT 2
78+#define OV2680_MIN_VBLANK 4
79+#define OV2680_MAX_VBLANK 0xffff
7880
7981 /* Fixed pre-div of 1/2 */
8082 #define OV2680_PLL_PREDIV0 2
@@ -84,10 +86,7 @@ · 3 unmodified lines
8486
8587 /* 66MHz pixel clock: 66MHz / 1704 * 1294 = 30fps */
8688 #define OV2680_PIXELS_PER_LINE 1704
87−#define OV2680_LINES_PER_FRAME 1294
88−
89−/* If possible send 16 extra rows / lines to the ISP as padding */
90−#define OV2680_END_MARGIN 16
89+#define OV2680_LINES_PER_FRAME_30FPS 1294
9190
9291 /* Max exposure time is VTS - 8 */
9392 #define OV2680_INTEGRATION_TIME_MARGIN 8
@@ -130,6 +129,8 @@ struct ov2680_ctrls { · 36 unmodified lines
130129 struct v4l2_ctrl *test_pattern;
131130 struct v4l2_ctrl *link_freq;
132131 struct v4l2_ctrl *pixel_rate;
132+ struct v4l2_ctrl *vblank;
133+ struct v4l2_ctrl *hblank;
133134 };
134135
135136 struct ov2680_mode {
@@ -143,8 +144,6 @@ struct ov2680_mode { · 7 unmodified lines
143144 u16 v_end;
144145 u16 h_output_size;
145146 u16 v_output_size;
146− u16 hts;
147− u16 vts;
148147 };
149148
150149 struct ov2680_dev {
@@ -359,15 +358,11 @@ static void ov2680_calc_mode(struct ov2680_dev *sensor) · 208 unmodified lines
359358 sensor->mode.v_start = (sensor->mode.crop.top +
360359 (sensor->mode.crop.height - height) / 2) & ~1;
361360 sensor->mode.h_end =
362− min(sensor->mode.h_start + width + OV2680_END_MARGIN - 1,
363− OV2680_NATIVE_WIDTH - 1);
361+ min(sensor->mode.h_start + width - 1, OV2680_NATIVE_WIDTH - 1);
364362 sensor->mode.v_end =
365− min(sensor->mode.v_start + height + OV2680_END_MARGIN - 1,
366− OV2680_NATIVE_HEIGHT - 1);
363+ min(sensor->mode.v_start + height - 1, OV2680_NATIVE_HEIGHT - 1);
367364 sensor->mode.h_output_size = orig_width;
368365 sensor->mode.v_output_size = orig_height;
369− sensor->mode.hts = OV2680_PIXELS_PER_LINE;
370− sensor->mode.vts = OV2680_LINES_PER_FRAME;
371366 }
372367
373368 static int ov2680_set_mode(struct ov2680_dev *sensor)
@@ -402,9 +397,8 @@ static int ov2680_set_mode(struct ov2680_dev *sensor) · 28 unmodified lines
402397 cci_write(sensor->regmap, OV2680_REG_VERTICAL_OUTPUT_SIZE,
403398 sensor->mode.v_output_size, &ret);
404399 cci_write(sensor->regmap, OV2680_REG_TIMING_HTS,
405− sensor->mode.hts, &ret);
406− cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
407− sensor->mode.vts, &ret);
400+ OV2680_PIXELS_PER_LINE, &ret);
401+ /* VTS gets set by the vblank ctrl */
408402 cci_write(sensor->regmap, OV2680_REG_ISP_X_WIN, 0, &ret);
409403 cci_write(sensor->regmap, OV2680_REG_ISP_Y_WIN, 0, &ret);
410404 cci_write(sensor->regmap, OV2680_REG_X_INC, inc, &ret);
@@ -478,6 +472,15 @@ static int ov2680_exposure_set(struct ov2680_dev *sensor, u32 exp) · 67 unmodified lines
478472 NULL);
479473 }
480474
475+static int ov2680_exposure_update_range(struct ov2680_dev *sensor)
476+{
477+ int exp_max = sensor->mode.fmt.height + sensor->ctrls.vblank->val -
478+ OV2680_INTEGRATION_TIME_MARGIN;
479+
480+ return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
481+ 1, exp_max);
482+}
483+
481484 static int ov2680_stream_enable(struct ov2680_dev *sensor)
482485 {
483486 int ret;
@@ -644,7 +647,7 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 160 unmodified lines
644647 struct v4l2_mbus_framefmt *try_fmt;
645648 const struct v4l2_rect *crop;
646649 unsigned int width, height;
647− int ret = 0;
650+ int def, max, ret = 0;
648651
649652 crop = __ov2680_get_pad_crop(sensor, sd_state, format->pad,
650653 format->which);
@@ -673,6 +676,27 @@ static int ov2680_set_fmt(struct v4l2_subdev *sd, · 22 unmodified lines
673676 sensor->mode.fmt = format->format;
674677 ov2680_calc_mode(sensor);
675678
679+ /* vblank range is height dependent adjust and reset to default */
680+ max = OV2680_MAX_VBLANK - height;
681+ def = OV2680_LINES_PER_FRAME_30FPS - height;
682+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV2680_MIN_VBLANK,
683+ max, 1, def);
684+ if (ret)
685+ goto unlock;
686+
687+ ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, def);
688+ if (ret)
689+ goto unlock;
690+
691+ /* exposure range depends on vts which may have changed */
692+ ret = ov2680_exposure_update_range(sensor);
693+ if (ret)
694+ goto unlock;
695+
696+ /* adjust hblank value for new width */
697+ def = OV2680_PIXELS_PER_LINE - width;
698+ ret = __v4l2_ctrl_modify_range(sensor->ctrls.hblank, def, def, 1, def);
699+
676700 unlock:
677701 mutex_unlock(&sensor->lock);
678702
@@ -842,6 +866,13 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) · 163 unmodified lines
842866 struct ov2680_dev *sensor = to_ov2680_dev(sd);
843867 int ret;
844868
869+ /* Update exposure range on vblank changes */
870+ if (ctrl->id == V4L2_CID_VBLANK) {
871+ ret = ov2680_exposure_update_range(sensor);
872+ if (ret)
873+ return ret;
874+ }
875+
845876 /* Only apply changes to the controls if the device is powered up */
846877 if (!pm_runtime_get_if_in_use(sensor->sd.dev)) {
847878 ov2680_set_bayer_order(sensor, &sensor->mode.fmt);
@@ -864,6 +895,10 @@ static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) · 16 unmodified lines
864895 case V4L2_CID_TEST_PATTERN:
865896 ret = ov2680_test_pattern_set(sensor, ctrl->val);
866897 break;
898+ case V4L2_CID_VBLANK:
899+ ret = cci_write(sensor->regmap, OV2680_REG_TIMING_VTS,
900+ sensor->mode.fmt.height + ctrl->val, NULL);
901+ break;
867902 default:
868903 ret = -EINVAL;
869904 break;
@@ -922,8 +957,8 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 52 unmodified lines
922957 const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops;
923958 struct ov2680_ctrls *ctrls = &sensor->ctrls;
924959 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
925− int exp_max = OV2680_LINES_PER_FRAME - OV2680_INTEGRATION_TIME_MARGIN;
926− int ret = 0;
960+ struct v4l2_fwnode_device_properties props;
961+ int def, max, ret = 0;
927962
928963 v4l2_i2c_subdev_init(&sensor->sd, client, &ov2680_subdev_ops);
929964 sensor->sd.internal_ops = &ov2680_internal_ops;
@@ -948,8 +983,9 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 18 unmodified lines
948983 ARRAY_SIZE(test_pattern_menu) - 1,
949984 0, 0, test_pattern_menu);
950985
986+ max = OV2680_LINES_PER_FRAME_30FPS - OV2680_INTEGRATION_TIME_MARGIN;
951987 ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
952− 0, exp_max, 1, exp_max);
988+ 0, max, 1, max);
953989
954990 ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
955991 0, 1023, 1, 250);
@@ -960,6 +996,21 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 4 unmodified lines
960996 0, sensor->pixel_rate,
961997 1, sensor->pixel_rate);
962998
999+ max = OV2680_MAX_VBLANK - OV2680_DEFAULT_HEIGHT;
1000+ def = OV2680_LINES_PER_FRAME_30FPS - OV2680_DEFAULT_HEIGHT;
1001+ ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
1002+ OV2680_MIN_VBLANK, max, 1, def);
1003+
1004+ def = OV2680_PIXELS_PER_LINE - OV2680_DEFAULT_WIDTH;
1005+ ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
1006+ def, def, 1, def);
1007+
1008+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
1009+ if (ret)
1010+ goto cleanup_entity;
1011+
1012+ v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
1013+
9631014 if (hdl->error) {
9641015 ret = hdl->error;
9651016 goto cleanup_entity;
@@ -968,6 +1019,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) · 2 unmodified lines
9681019 ctrls->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
9691020 ctrls->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
9701021 ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
1022+ ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
9711023
9721024 sensor->sd.ctrl_handler = hdl;
9731025
@@ -1116,25 +1168,24 @@ static int ov2680_parse_dt(struct ov2680_dev *sensor) · 142 unmodified lines
11161168 sensor->pixel_rate = sensor->link_freq[0] * 2;
11171169 do_div(sensor->pixel_rate, 10);
11181170
1119− /* Verify bus cfg */
1120− if (bus_cfg.bus.mipi_csi2.num_data_lanes != 1) {
1121− ret = dev_err_probe(dev, -EINVAL,
1122− "only a 1-lane CSI2 config is supported");
1123− goto out_free_bus_cfg;
1171+ if (!bus_cfg.nr_of_link_frequencies) {
1172+ dev_warn(dev, "Consider passing 'link-frequencies' in DT\n");
1173+ goto skip_link_freq_validation;
11241174 }
11251175
11261176 for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++)
11271177 if (bus_cfg.link_frequencies[i] == sensor->link_freq[0])
11281178 break;
11291179
1130− if (bus_cfg.nr_of_link_frequencies == 0 ||
1131− bus_cfg.nr_of_link_frequencies == i) {
1180+ if (bus_cfg.nr_of_link_frequencies == i) {
11321181 ret = dev_err_probe(dev, -EINVAL,
11331182 "supported link freq %lld not found\n",
11341183 sensor->link_freq[0]);
11351184 goto out_free_bus_cfg;
11361185 }
11371186
1187+skip_link_freq_validation:
1188+ ret = 0;
11381189 out_free_bus_cfg:
11391190 v4l2_fwnode_endpoint_free(&bus_cfg);
11401191 return ret;
drivers/media/i2c/ov2740.c+14−6
@@ -768,14 +768,15 @@ static int ov2740_init_controls(struct ov2740 *ov2740) · 767 unmodified lines
768768 cur_mode = ov2740->cur_mode;
769769 size = ARRAY_SIZE(link_freq_menu_items);
770770
771− ov2740->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
772− V4L2_CID_LINK_FREQ,
773− size - 1, 0,
774− link_freq_menu_items);
771+ ov2740->link_freq =
772+ v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
773+ V4L2_CID_LINK_FREQ, size - 1,
774+ ov2740->supported_modes->link_freq_index,
775+ link_freq_menu_items);
775776 if (ov2740->link_freq)
776777 ov2740->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
777778
778− pixel_rate = to_pixel_rate(OV2740_LINK_FREQ_360MHZ_INDEX);
779+ pixel_rate = to_pixel_rate(ov2740->supported_modes->link_freq_index);
779780 ov2740->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
780781 V4L2_CID_PIXEL_RATE, 0,
781782 pixel_rate, 1, pixel_rate);
@@ -1332,9 +1333,16 @@ static int ov2740_probe(struct i2c_client *client) · 550 unmodified lines
13321333 return dev_err_probe(dev, ret, "failed to check HW configuration\n");
13331334
13341335 ov2740->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
1335− if (IS_ERR(ov2740->reset_gpio))
1336+ if (IS_ERR(ov2740->reset_gpio)) {
13361337 return dev_err_probe(dev, PTR_ERR(ov2740->reset_gpio),
13371338 "failed to get reset GPIO\n");
1339+ } else if (ov2740->reset_gpio) {
1340+ /*
1341+ * Ensure reset is asserted for at least 20 ms before
1342+ * ov2740_resume() deasserts it.
1343+ */
1344+ msleep(20);
1345+ }
13381346
13391347 ov2740->clk = devm_clk_get_optional(dev, "clk");
13401348 if (IS_ERR(ov2740->clk))
drivers/media/i2c/ov4689.c+359−314
@@ -3,7 +3,7 @@ · 2 unmodified lines
33 * ov4689 driver
44 *
55 * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
6− * Copyright (C) 2022 Mikhail Rudenko
6+ * Copyright (C) 2022, 2024 Mikhail Rudenko
77 */
88
99 #include <linux/clk.h>
@@ -15,45 +15,86 @@ · 5 unmodified lines
1515 #include <linux/regulator/consumer.h>
1616 #include <media/media-entity.h>
1717 #include <media/v4l2-async.h>
18+#include <media/v4l2-cci.h>
1819 #include <media/v4l2-ctrls.h>
1920 #include <media/v4l2-subdev.h>
2021 #include <media/v4l2-fwnode.h>
2122
22−#define CHIP_ID 0x004688
23−#define OV4689_REG_CHIP_ID 0x300a
24−
25−#define OV4689_XVCLK_FREQ 24000000
26−
27−#define OV4689_REG_CTRL_MODE 0x0100
23+#define OV4689_REG_CTRL_MODE CCI_REG8(0x0100)
2824 #define OV4689_MODE_SW_STANDBY 0x0
2925 #define OV4689_MODE_STREAMING BIT(0)
3026
31−#define OV4689_REG_EXPOSURE 0x3500
27+#define OV4689_REG_CHIP_ID CCI_REG16(0x300a)
28+#define CHIP_ID 0x004688
29+
30+#define OV4689_REG_EXPOSURE CCI_REG24(0x3500)
3231 #define OV4689_EXPOSURE_MIN 4
3332 #define OV4689_EXPOSURE_STEP 1
34−#define OV4689_VTS_MAX 0x7fff
3533
36−#define OV4689_REG_GAIN_H 0x3508
37−#define OV4689_REG_GAIN_L 0x3509
38−#define OV4689_GAIN_H_MASK 0x07
39−#define OV4689_GAIN_H_SHIFT 8
40−#define OV4689_GAIN_L_MASK 0xff
34+#define OV4689_REG_GAIN CCI_REG16(0x3508)
4135 #define OV4689_GAIN_STEP 1
4236 #define OV4689_GAIN_DEFAULT 0x80
4337
44−#define OV4689_REG_TEST_PATTERN 0x5040
45−#define OV4689_TEST_PATTERN_ENABLE 0x80
46−#define OV4689_TEST_PATTERN_DISABLE 0x0
38+#define OV4689_REG_DIG_GAIN CCI_REG16(0x352a)
39+#define OV4689_DIG_GAIN_MIN 1
40+#define OV4689_DIG_GAIN_MAX 0x7fff
41+#define OV4689_DIG_GAIN_STEP 1
42+#define OV4689_DIG_GAIN_DEFAULT 0x800
4743
48−#define OV4689_REG_VTS 0x380e
44+#define OV4689_REG_H_CROP_START CCI_REG16(0x3800)
45+#define OV4689_REG_V_CROP_START CCI_REG16(0x3802)
46+#define OV4689_REG_H_CROP_END CCI_REG16(0x3804)
47+#define OV4689_REG_V_CROP_END CCI_REG16(0x3806)
48+#define OV4689_REG_H_OUTPUT_SIZE CCI_REG16(0x3808)
49+#define OV4689_REG_V_OUTPUT_SIZE CCI_REG16(0x380a)
4950
50−#define REG_NULL 0xFFFF
51+#define OV4689_REG_HTS CCI_REG16(0x380c)
52+#define OV4689_HTS_DIVIDER 4
53+#define OV4689_HTS_MAX 0x7fff
5154
52−#define OV4689_REG_VALUE_08BIT 1
53−#define OV4689_REG_VALUE_16BIT 2
54−#define OV4689_REG_VALUE_24BIT 3
55+#define OV4689_REG_VTS CCI_REG16(0x380e)
56+#define OV4689_VTS_MAX 0x7fff
57+
58+#define OV4689_REG_H_WIN_OFF CCI_REG16(0x3810)
59+#define OV4689_REG_V_WIN_OFF CCI_REG16(0x3812)
60+
61+#define OV4689_REG_TIMING_FORMAT1 CCI_REG8(0x3820) /* Vertical */
62+#define OV4689_REG_TIMING_FORMAT2 CCI_REG8(0x3821) /* Horizontal */
63+#define OV4689_TIMING_FLIP_MASK GENMASK(2, 1)
64+#define OV4689_TIMING_FLIP_ARRAY BIT(1)
65+#define OV4689_TIMING_FLIP_DIGITAL BIT(2)
66+#define OV4689_TIMING_FLIP_BOTH (OV4689_TIMING_FLIP_ARRAY |\
67+ OV4689_TIMING_FLIP_DIGITAL)
68+
69+#define OV4689_REG_ANCHOR_LEFT_START CCI_REG16(0x4020)
70+#define OV4689_ANCHOR_LEFT_START_DEF 576
71+#define OV4689_REG_ANCHOR_LEFT_END CCI_REG16(0x4022)
72+#define OV4689_ANCHOR_LEFT_END_DEF 831
73+#define OV4689_REG_ANCHOR_RIGHT_START CCI_REG16(0x4024)
74+#define OV4689_ANCHOR_RIGHT_START_DEF 1984
75+#define OV4689_REG_ANCHOR_RIGHT_END CCI_REG16(0x4026)
76+#define OV4689_ANCHOR_RIGHT_END_DEF 2239
77+
78+#define OV4689_REG_VFIFO_CTRL_01 CCI_REG8(0x4601)
79+
80+#define OV4689_REG_WB_GAIN_RED CCI_REG16(0x500c)
81+#define OV4689_REG_WB_GAIN_BLUE CCI_REG16(0x5010)
82+#define OV4689_WB_GAIN_MIN 1
83+#define OV4689_WB_GAIN_MAX 0xfff
84+#define OV4689_WB_GAIN_STEP 1
85+#define OV4689_WB_GAIN_DEFAULT 0x400
86+
87+#define OV4689_REG_TEST_PATTERN CCI_REG8(0x5040)
88+#define OV4689_TEST_PATTERN_ENABLE 0x80
89+#define OV4689_TEST_PATTERN_DISABLE 0x0
5590
5691 #define OV4689_LANES 4
92+#define OV4689_XVCLK_FREQ 24000000
93+
94+#define OV4689_PIXEL_ARRAY_WIDTH 2720
95+#define OV4689_PIXEL_ARRAY_HEIGHT 1536
96+#define OV4689_DUMMY_ROWS 8 /* 8 dummy rows on each side */
97+#define OV4689_DUMMY_COLUMNS 16 /* 16 dummy columns on each side */
5798
5899 static const char *const ov4689_supply_names[] = {
59100 "avdd", /* Analog power */
@@ -61,11 +102,6 @@ static const char *const ov4689_supply_names[] = { · 1 unmodified line
61102 "dvdd", /* Digital core power */
62103 };
63104
64−struct regval {
65− u16 addr;
66− u8 val;
67−};
68−
69105 enum ov4689_mode_id {
70106 OV4689_MODE_2688_1520 = 0,
71107 OV4689_NUM_MODES,
@@ -75,20 +111,18 @@ struct ov4689_mode { · 3 unmodified lines
75111 enum ov4689_mode_id id;
76112 u32 width;
77113 u32 height;
78− u32 max_fps;
79114 u32 hts_def;
115+ u32 hts_min;
80116 u32 vts_def;
81117 u32 exp_def;
82118 u32 pixel_rate;
83− u32 sensor_width;
84− u32 sensor_height;
85− u32 crop_top;
86− u32 crop_left;
87− const struct regval *reg_list;
119+ const struct cci_reg_sequence *reg_list;
120+ unsigned int num_regs;
88121 };
89122
90123 struct ov4689 {
91− struct i2c_client *client;
124+ struct device *dev;
125+ struct regmap *regmap;
92126 struct clk *xvclk;
93127 struct gpio_desc *reset_gpio;
94128 struct gpio_desc *pwdn_gpio;
@@ -99,7 +133,6 @@ struct ov4689 { · 4 unmodified lines
99133
100134 u32 clock_rate;
101135
102− struct mutex mutex; /* lock to protect ctrls and cur_mode */
103136 struct v4l2_ctrl_handler ctrl_handler;
104137 struct v4l2_ctrl *exposure;
105138
@@ -119,95 +152,108 @@ struct ov4689_gain_range { · 13 unmodified lines
119152
120153 /*
121154 * Xclk 24Mhz
122− * max_framerate 30fps
155+ * max_framerate 90fps
123156 * mipi_datarate per lane 1008Mbps
124157 */
125−static const struct regval ov4689_2688x1520_regs[] = {
126− {0x0103, 0x01}, {0x3638, 0x00}, {0x0300, 0x00},
127− {0x0302, 0x2a}, {0x0303, 0x00}, {0x0304, 0x03},
128− {0x030b, 0x00}, {0x030d, 0x1e}, {0x030e, 0x04},
129− {0x030f, 0x01}, {0x0312, 0x01}, {0x031e, 0x00},
130− {0x3000, 0x20}, {0x3002, 0x00}, {0x3018, 0x72},
131− {0x3020, 0x93}, {0x3021, 0x03}, {0x3022, 0x01},
132− {0x3031, 0x0a}, {0x303f, 0x0c}, {0x3305, 0xf1},
133− {0x3307, 0x04}, {0x3309, 0x29}, {0x3500, 0x00},
134− {0x3501, 0x60}, {0x3502, 0x00}, {0x3503, 0x04},
135− {0x3504, 0x00}, {0x3505, 0x00}, {0x3506, 0x00},
136− {0x3507, 0x00}, {0x3508, 0x00}, {0x3509, 0x80},
137− {0x350a, 0x00}, {0x350b, 0x00}, {0x350c, 0x00},
138− {0x350d, 0x00}, {0x350e, 0x00}, {0x350f, 0x80},
139− {0x3510, 0x00}, {0x3511, 0x00}, {0x3512, 0x00},
140− {0x3513, 0x00}, {0x3514, 0x00}, {0x3515, 0x80},
141− {0x3516, 0x00}, {0x3517, 0x00}, {0x3518, 0x00},
142− {0x3519, 0x00}, {0x351a, 0x00}, {0x351b, 0x80},
143− {0x351c, 0x00}, {0x351d, 0x00}, {0x351e, 0x00},
144− {0x351f, 0x00}, {0x3520, 0x00}, {0x3521, 0x80},
145− {0x3522, 0x08}, {0x3524, 0x08}, {0x3526, 0x08},
146− {0x3528, 0x08}, {0x352a, 0x08}, {0x3602, 0x00},
147− {0x3603, 0x40}, {0x3604, 0x02}, {0x3605, 0x00},
148− {0x3606, 0x00}, {0x3607, 0x00}, {0x3609, 0x12},
149− {0x360a, 0x40}, {0x360c, 0x08}, {0x360f, 0xe5},
150− {0x3608, 0x8f}, {0x3611, 0x00}, {0x3613, 0xf7},
151− {0x3616, 0x58}, {0x3619, 0x99}, {0x361b, 0x60},
152− {0x361c, 0x7a}, {0x361e, 0x79}, {0x361f, 0x02},
153− {0x3632, 0x00}, {0x3633, 0x10}, {0x3634, 0x10},
154− {0x3635, 0x10}, {0x3636, 0x15}, {0x3646, 0x86},
155− {0x364a, 0x0b}, {0x3700, 0x17}, {0x3701, 0x22},
156− {0x3703, 0x10}, {0x370a, 0x37}, {0x3705, 0x00},
157− {0x3706, 0x63}, {0x3709, 0x3c}, {0x370b, 0x01},
158− {0x370c, 0x30}, {0x3710, 0x24}, {0x3711, 0x0c},
159− {0x3716, 0x00}, {0x3720, 0x28}, {0x3729, 0x7b},
160− {0x372a, 0x84}, {0x372b, 0xbd}, {0x372c, 0xbc},
161− {0x372e, 0x52}, {0x373c, 0x0e}, {0x373e, 0x33},
162− {0x3743, 0x10}, {0x3744, 0x88}, {0x3745, 0xc0},
163− {0x374a, 0x43}, {0x374c, 0x00}, {0x374e, 0x23},
164− {0x3751, 0x7b}, {0x3752, 0x84}, {0x3753, 0xbd},
165− {0x3754, 0xbc}, {0x3756, 0x52}, {0x375c, 0x00},
166− {0x3760, 0x00}, {0x3761, 0x00}, {0x3762, 0x00},
167− {0x3763, 0x00}, {0x3764, 0x00}, {0x3767, 0x04},
168− {0x3768, 0x04}, {0x3769, 0x08}, {0x376a, 0x08},
169− {0x376b, 0x20}, {0x376c, 0x00}, {0x376d, 0x00},
170− {0x376e, 0x00}, {0x3773, 0x00}, {0x3774, 0x51},
171− {0x3776, 0xbd}, {0x3777, 0xbd}, {0x3781, 0x18},
172− {0x3783, 0x25}, {0x3798, 0x1b}, {0x3800, 0x00},
173− {0x3801, 0x08}, {0x3802, 0x00}, {0x3803, 0x04},
174− {0x3804, 0x0a}, {0x3805, 0x97}, {0x3806, 0x05},
175− {0x3807, 0xfb}, {0x3808, 0x0a}, {0x3809, 0x80},
176− {0x380a, 0x05}, {0x380b, 0xf0}, {0x380c, 0x0a},
177− {0x380d, 0x0e}, {0x380e, 0x06}, {0x380f, 0x12},
178− {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00},
179− {0x3813, 0x04}, {0x3814, 0x01}, {0x3815, 0x01},
180− {0x3819, 0x01}, {0x3820, 0x00}, {0x3821, 0x06},
181− {0x3829, 0x00}, {0x382a, 0x01}, {0x382b, 0x01},
182− {0x382d, 0x7f}, {0x3830, 0x04}, {0x3836, 0x01},
183− {0x3837, 0x00}, {0x3841, 0x02}, {0x3846, 0x08},
184− {0x3847, 0x07}, {0x3d85, 0x36}, {0x3d8c, 0x71},
185− {0x3d8d, 0xcb}, {0x3f0a, 0x00}, {0x4000, 0xf1},
186− {0x4001, 0x40}, {0x4002, 0x04}, {0x4003, 0x14},
187− {0x400e, 0x00}, {0x4011, 0x00}, {0x401a, 0x00},
188− {0x401b, 0x00}, {0x401c, 0x00}, {0x401d, 0x00},
189− {0x401f, 0x00}, {0x4020, 0x00}, {0x4021, 0x10},
190− {0x4022, 0x07}, {0x4023, 0xcf}, {0x4024, 0x09},
191− {0x4025, 0x60}, {0x4026, 0x09}, {0x4027, 0x6f},
192− {0x4028, 0x00}, {0x4029, 0x02}, {0x402a, 0x06},
193− {0x402b, 0x04}, {0x402c, 0x02}, {0x402d, 0x02},
194− {0x402e, 0x0e}, {0x402f, 0x04}, {0x4302, 0xff},
195− {0x4303, 0xff}, {0x4304, 0x00}, {0x4305, 0x00},
196− {0x4306, 0x00}, {0x4308, 0x02}, {0x4500, 0x6c},
197− {0x4501, 0xc4}, {0x4502, 0x40}, {0x4503, 0x01},
198− {0x4601, 0xa7}, {0x4800, 0x04}, {0x4813, 0x08},
199− {0x481f, 0x40}, {0x4829, 0x78}, {0x4837, 0x10},
200− {0x4b00, 0x2a}, {0x4b0d, 0x00}, {0x4d00, 0x04},
201− {0x4d01, 0x42}, {0x4d02, 0xd1}, {0x4d03, 0x93},
202− {0x4d04, 0xf5}, {0x4d05, 0xc1}, {0x5000, 0xf3},
203− {0x5001, 0x11}, {0x5004, 0x00}, {0x500a, 0x00},
204− {0x500b, 0x00}, {0x5032, 0x00}, {0x5040, 0x00},
205− {0x5050, 0x0c}, {0x5500, 0x00}, {0x5501, 0x10},
206− {0x5502, 0x01}, {0x5503, 0x0f}, {0x8000, 0x00},
207− {0x8001, 0x00}, {0x8002, 0x00}, {0x8003, 0x00},
208− {0x8004, 0x00}, {0x8005, 0x00}, {0x8006, 0x00},
209− {0x8007, 0x00}, {0x8008, 0x00}, {0x3638, 0x00},
210− {REG_NULL, 0x00},
158+static const struct cci_reg_sequence ov4689_2688x1520_regs[] = {
159+ /* System control*/
160+ { CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */
161+ { CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */
162+ { CCI_REG8(0x3021), 0x03 }, /*
163+ * SC_CMMN_MISC_CTRL fst_stby_ctr = 0,
164+ * sleep_no_latch_enable = 0
165+ */
166+
167+ /* AEC PK */
168+ { CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */
169+
170+ /* ADC and analog control*/
171+ { CCI_REG8(0x3603), 0x40 },
172+ { CCI_REG8(0x3604), 0x02 },
173+ { CCI_REG8(0x3609), 0x12 },
174+ { CCI_REG8(0x360c), 0x08 },
175+ { CCI_REG8(0x360f), 0xe5 },
176+ { CCI_REG8(0x3608), 0x8f },
177+ { CCI_REG8(0x3611), 0x00 },
178+ { CCI_REG8(0x3613), 0xf7 },
179+ { CCI_REG8(0x3616), 0x58 },
180+ { CCI_REG8(0x3619), 0x99 },
181+ { CCI_REG8(0x361b), 0x60 },
182+ { CCI_REG8(0x361e), 0x79 },
183+ { CCI_REG8(0x3634), 0x10 },
184+ { CCI_REG8(0x3635), 0x10 },
185+ { CCI_REG8(0x3636), 0x15 },
186+ { CCI_REG8(0x3646), 0x86 },
187+ { CCI_REG8(0x364a), 0x0b },
188+
189+ /* Sensor control */
190+ { CCI_REG8(0x3700), 0x17 },
191+ { CCI_REG8(0x3701), 0x22 },
192+ { CCI_REG8(0x3703), 0x10 },
193+ { CCI_REG8(0x370a), 0x37 },
194+ { CCI_REG8(0x3706), 0x63 },
195+ { CCI_REG8(0x3709), 0x3c },
196+ { CCI_REG8(0x370c), 0x30 },
197+ { CCI_REG8(0x3710), 0x24 },
198+ { CCI_REG8(0x3720), 0x28 },
199+ { CCI_REG8(0x3729), 0x7b },
200+ { CCI_REG8(0x372b), 0xbd },
201+ { CCI_REG8(0x372c), 0xbc },
202+ { CCI_REG8(0x372e), 0x52 },
203+ { CCI_REG8(0x373c), 0x0e },
204+ { CCI_REG8(0x373e), 0x33 },
205+ { CCI_REG8(0x3743), 0x10 },
206+ { CCI_REG8(0x3744), 0x88 },
207+ { CCI_REG8(0x3745), 0xc0 },
208+ { CCI_REG8(0x374c), 0x00 },
209+ { CCI_REG8(0x374e), 0x23 },
210+ { CCI_REG8(0x3751), 0x7b },
211+ { CCI_REG8(0x3753), 0xbd },
212+ { CCI_REG8(0x3754), 0xbc },
213+ { CCI_REG8(0x3756), 0x52 },
214+ { CCI_REG8(0x376b), 0x20 },
215+ { CCI_REG8(0x3774), 0x51 },
216+ { CCI_REG8(0x3776), 0xbd },
217+ { CCI_REG8(0x3777), 0xbd },
218+ { CCI_REG8(0x3781), 0x18 },
219+ { CCI_REG8(0x3783), 0x25 },
220+ { CCI_REG8(0x3798), 0x1b },
221+
222+ /* Timing control */
223+ { CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */
224+
225+ /* OTP control */
226+ { CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1,
227+ * OTP_power_up_load_data_enable = 1,
228+ * OTP_bist_select = 1 (compare with zero)
229+ */
230+ { CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */
231+ { CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */
232+
233+ /* BLC registers*/
234+ { CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */
235+ { CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */
236+ { CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */
237+ { CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */
238+
239+ /* ADC sync control */
240+ { CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */
241+ { CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */
242+
243+ /* Temperature monitor */
244+ { CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */
245+ { CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */
246+ { CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */
247+ { CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */
248+ { CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8] = 0xf5 */
249+ { CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0] = 0xc1 */
250+
251+ /* pre-ISP control */
252+ { CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */
253+
254+ /* OTP-DPC control */
255+ { CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */
256+ { CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */
211257 };
212258
213259 static const struct ov4689_mode supported_modes[] = {
@@ -215,16 +261,13 @@ static const struct ov4689_mode supported_modes[] = { · 1 unmodified line
215261 .id = OV4689_MODE_2688_1520,
216262 .width = 2688,
217263 .height = 1520,
218− .sensor_width = 2720,
219− .sensor_height = 1536,
220− .crop_top = 8,
221− .crop_left = 16,
222− .max_fps = 30,
223264 .exp_def = 1536,
224− .hts_def = 4 * 2574,
265+ .hts_def = 10296,
266+ .hts_min = 3432,
225267 .vts_def = 1554,
226268 .pixel_rate = 480000000,
227269 .reg_list = ov4689_2688x1520_regs,
270+ .num_regs = ARRAY_SIZE(ov4689_2688x1520_regs),
228271 },
229272 };
230273
@@ -277,83 +320,6 @@ static const struct ov4689_gain_range ov4689_gain_ranges[] = { · 46 unmodified lines
277320 },
278321 };
279322
280−/* Write registers up to 4 at a time */
281−static int ov4689_write_reg(struct i2c_client *client, u16 reg, u32 len,
282− u32 val)
283−{
284− u32 buf_i, val_i;
285− __be32 val_be;
286− u8 *val_p;
287− u8 buf[6];
288−
289− if (len > 4)
290− return -EINVAL;
291−
292− buf[0] = reg >> 8;
293− buf[1] = reg & 0xff;
294−
295− val_be = cpu_to_be32(val);
296− val_p = (u8 *)&val_be;
297− buf_i = 2;
298− val_i = 4 - len;
299−
300− while (val_i < 4)
301− buf[buf_i++] = val_p[val_i++];
302−
303− if (i2c_master_send(client, buf, len + 2) != len + 2)
304− return -EIO;
305−
306− return 0;
307−}
308−
309−static int ov4689_write_array(struct i2c_client *client,
310− const struct regval *regs)
311−{
312− int ret = 0;
313− u32 i;
314−
315− for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
316− ret = ov4689_write_reg(client, regs[i].addr,
317− OV4689_REG_VALUE_08BIT, regs[i].val);
318−
319− return ret;
320−}
321−
322−/* Read registers up to 4 at a time */
323−static int ov4689_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
324− u32 *val)
325−{
326− __be16 reg_addr_be = cpu_to_be16(reg);
327− struct i2c_msg msgs[2];
328− __be32 data_be = 0;
329− u8 *data_be_p;
330− int ret;
331−
332− if (len > 4 || !len)
333− return -EINVAL;
334−
335− data_be_p = (u8 *)&data_be;
336− /* Write register address */
337− msgs[0].addr = client->addr;
338− msgs[0].flags = 0;
339− msgs[0].len = 2;
340− msgs[0].buf = (u8 *)&reg_addr_be;
341−
342− /* Read data from register */
343− msgs[1].addr = client->addr;
344− msgs[1].flags = I2C_M_RD;
345− msgs[1].len = len;
346− msgs[1].buf = &data_be_p[4 - len];
347−
348− ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
349− if (ret != ARRAY_SIZE(msgs))
350− return -EIO;
351−
352− *val = be32_to_cpu(data_be);
353−
354− return 0;
355−}
356−
357323 static void ov4689_fill_fmt(const struct ov4689_mode *mode,
358324 struct v4l2_mbus_framefmt *fmt)
359325 {
@@ -376,19 +342,6 @@ static int ov4689_set_fmt(struct v4l2_subdev *sd, · 16 unmodified lines
376342 return 0;
377343 }
378344
379−static int ov4689_get_fmt(struct v4l2_subdev *sd,
380− struct v4l2_subdev_state *sd_state,
381− struct v4l2_subdev_format *fmt)
382−{
383− struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
384− struct ov4689 *ov4689 = to_ov4689(sd);
385−
386− /* only one mode supported for now */
387− ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
388−
389− return 0;
390−}
391−
392345 static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
393346 struct v4l2_subdev_state *sd_state,
394347 struct v4l2_subdev_mbus_code_enum *code)
@@ -427,16 +380,14 @@ static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern) · 32 unmodified lines
427380 else
428381 val = OV4689_TEST_PATTERN_DISABLE;
429382
430− return ov4689_write_reg(ov4689->client, OV4689_REG_TEST_PATTERN,
431− OV4689_REG_VALUE_08BIT, val);
383+ return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN,
384+ val, NULL);
432385 }
433386
434387 static int ov4689_get_selection(struct v4l2_subdev *sd,
435388 struct v4l2_subdev_state *state,
436389 struct v4l2_subdev_selection *sel)
437390 {
438− const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode;
439−
440391 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
441392 return -EINVAL;
442393
@@ -444,63 +395,114 @@ static int ov4689_get_selection(struct v4l2_subdev *sd, · 1 unmodified line
444395 case V4L2_SEL_TGT_CROP_BOUNDS:
445396 sel->r.top = 0;
446397 sel->r.left = 0;
447− sel->r.width = mode->sensor_width;
448− sel->r.height = mode->sensor_height;
398+ sel->r.width = OV4689_PIXEL_ARRAY_WIDTH;
399+ sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT;
449400 return 0;
450401 case V4L2_SEL_TGT_CROP:
451402 case V4L2_SEL_TGT_CROP_DEFAULT:
452− sel->r.top = mode->crop_top;
453− sel->r.left = mode->crop_left;
454− sel->r.width = mode->width;
455− sel->r.height = mode->height;
403+ sel->r.top = OV4689_DUMMY_ROWS;
404+ sel->r.left = OV4689_DUMMY_COLUMNS;
405+ sel->r.width =
406+ OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS;
407+ sel->r.height =
408+ OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS;
456409 return 0;
457410 }
458411
459412 return -EINVAL;
460413 }
461414
415+static int ov4689_setup_timings(struct ov4689 *ov4689)
416+{
417+ const struct ov4689_mode *mode = ov4689->cur_mode;
418+ struct regmap *rm = ov4689->regmap;
419+ int ret = 0;
420+
421+ cci_write(rm, OV4689_REG_H_CROP_START, 8, &ret);
422+ cci_write(rm, OV4689_REG_V_CROP_START, 8, &ret);
423+ cci_write(rm, OV4689_REG_H_CROP_END, 2711, &ret);
424+ cci_write(rm, OV4689_REG_V_CROP_END, 1531, &ret);
425+
426+ cci_write(rm, OV4689_REG_H_OUTPUT_SIZE, mode->width, &ret);
427+ cci_write(rm, OV4689_REG_V_OUTPUT_SIZE, mode->height, &ret);
428+
429+ cci_write(rm, OV4689_REG_H_WIN_OFF, 8, &ret);
430+ cci_write(rm, OV4689_REG_V_WIN_OFF, 4, &ret);
431+
432+ cci_write(rm, OV4689_REG_VFIFO_CTRL_01, 167, &ret);
433+
434+ return ret;
435+}
436+
437+static int ov4689_setup_blc_anchors(struct ov4689 *ov4689)
438+{
439+ struct regmap *rm = ov4689->regmap;
440+ int ret = 0;
441+
442+ cci_write(rm, OV4689_REG_ANCHOR_LEFT_START, 16, &ret);
443+ cci_write(rm, OV4689_REG_ANCHOR_LEFT_END, 1999, &ret);
444+ cci_write(rm, OV4689_REG_ANCHOR_RIGHT_START, 2400, &ret);
445+ cci_write(rm, OV4689_REG_ANCHOR_RIGHT_END, 2415, &ret);
446+
447+ return ret;
448+}
449+
462450 static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
463451 {
464452 struct ov4689 *ov4689 = to_ov4689(sd);
465− struct i2c_client *client = ov4689->client;
453+ struct v4l2_subdev_state *sd_state;
454+ struct device *dev = ov4689->dev;
466455 int ret = 0;
467456
468− mutex_lock(&ov4689->mutex);
457+ sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev);
469458
470459 if (on) {
471− ret = pm_runtime_resume_and_get(&client->dev);
460+ ret = pm_runtime_resume_and_get(dev);
472461 if (ret < 0)
473462 goto unlock_and_return;
474463
475− ret = ov4689_write_array(ov4689->client,
476− ov4689->cur_mode->reg_list);
464+ ret = cci_multi_reg_write(ov4689->regmap,
465+ ov4689->cur_mode->reg_list,
466+ ov4689->cur_mode->num_regs,
467+ NULL);
468+ if (ret) {
469+ pm_runtime_put(dev);
470+ goto unlock_and_return;
471+ }
472+
473+ ret = ov4689_setup_timings(ov4689);
477474 if (ret) {
478− pm_runtime_put(&client->dev);
475+ pm_runtime_put(dev);
476+ goto unlock_and_return;
477+ }
478+
479+ ret = ov4689_setup_blc_anchors(ov4689);
480+ if (ret) {
481+ pm_runtime_put(dev);
479482 goto unlock_and_return;
480483 }
481484
482485 ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
483486 if (ret) {
484− pm_runtime_put(&client->dev);
487+ pm_runtime_put(dev);
485488 goto unlock_and_return;
486489 }
487490
488− ret = ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
489− OV4689_REG_VALUE_08BIT,
490− OV4689_MODE_STREAMING);
491+ ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
492+ OV4689_MODE_STREAMING, NULL);
491493 if (ret) {
492− pm_runtime_put(&client->dev);
494+ pm_runtime_put(dev);
493495 goto unlock_and_return;
494496 }
495497 } else {
496− ov4689_write_reg(ov4689->client, OV4689_REG_CTRL_MODE,
497− OV4689_REG_VALUE_08BIT,
498− OV4689_MODE_SW_STANDBY);
499− pm_runtime_put(&client->dev);
498+ cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
499+ OV4689_MODE_SW_STANDBY, NULL);
500+ pm_runtime_mark_last_busy(dev);
501+ pm_runtime_put_autosuspend(dev);
500502 }
501503
502504 unlock_and_return:
503− mutex_unlock(&ov4689->mutex);
505+ v4l2_subdev_unlock_state(sd_state);
504506
505507 return ret;
506508 }
@@ -563,18 +565,13 @@ static int __maybe_unused ov4689_power_off(struct device *dev) · 56 unmodified lines
563565 return 0;
564566 }
565567
566−static int ov4689_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
568+static int ov4689_init_state(struct v4l2_subdev *sd,
569+ struct v4l2_subdev_state *sd_state)
567570 {
568− struct ov4689 *ov4689 = to_ov4689(sd);
569− struct v4l2_mbus_framefmt *try_fmt;
570−
571− mutex_lock(&ov4689->mutex);
572−
573− try_fmt = v4l2_subdev_state_get_format(fh->state, 0);
574− /* Initialize try_fmt */
575− ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], try_fmt);
571+ struct v4l2_mbus_framefmt *fmt =
572+ v4l2_subdev_state_get_format(sd_state, 0);
576573
577− mutex_unlock(&ov4689->mutex);
574+ ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt);
578575
579576 return 0;
580577 }
@@ -583,10 +580,6 @@ static const struct dev_pm_ops ov4689_pm_ops = { · 2 unmodified lines
583580 SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL)
584581 };
585582
586−static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
587− .open = ov4689_open,
588−};
589−
590583 static const struct v4l2_subdev_video_ops ov4689_video_ops = {
591584 .s_stream = ov4689_s_stream,
592585 };
@@ -594,11 +587,15 @@ static const struct v4l2_subdev_video_ops ov4689_video_ops = { · 1 unmodified line
594587 static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
595588 .enum_mbus_code = ov4689_enum_mbus_code,
596589 .enum_frame_size = ov4689_enum_frame_sizes,
597− .get_fmt = ov4689_get_fmt,
590+ .get_fmt = v4l2_subdev_get_fmt,
598591 .set_fmt = ov4689_set_fmt,
599592 .get_selection = ov4689_get_selection,
600593 };
601594
595+static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
596+ .init_state = ov4689_init_state,
597+};
598+
602599 static const struct v4l2_subdev_ops ov4689_subdev_ops = {
603600 .video = &ov4689_video_ops,
604601 .pad = &ov4689_pad_ops,
@@ -610,7 +607,6 @@ static const struct v4l2_subdev_ops ov4689_subdev_ops = { · 5 unmodified lines
610607 */
611608 static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
612609 {
613− const struct device *dev = &ov4689->client->dev;
614610 const struct ov4689_gain_range *range;
615611 unsigned int n;
616612
@@ -621,7 +617,8 @@ static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result) · 4 unmodified lines
621617 }
622618
623619 if (n == ARRAY_SIZE(ov4689_gain_ranges)) {
624− dev_warn_ratelimited(dev, "no mapping found for gain %d\n",
620+ dev_warn_ratelimited(ov4689->dev,
621+ "no mapping found for gain %d\n",
625622 logical_gain);
626623 return -EINVAL;
627624 }
@@ -637,10 +634,11 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 9 unmodified lines
637634 {
638635 struct ov4689 *ov4689 =
639636 container_of(ctrl->handler, struct ov4689, ctrl_handler);
640− struct i2c_client *client = ov4689->client;
641− int sensor_gain;
637+ struct regmap *regmap = ov4689->regmap;
638+ struct device *dev = ov4689->dev;
639+ int sensor_gain = 0;
642640 s64 max_expo;
643− int ret;
641+ int ret = 0;
644642
645643 /* Propagate change of current control to all related controls */
646644 switch (ctrl->id) {
@@ -654,44 +652,58 @@ static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl) · 7 unmodified lines
654652 break;
655653 }
656654
657− if (!pm_runtime_get_if_in_use(&client->dev))
655+ if (!pm_runtime_get_if_in_use(dev))
658656 return 0;
659657
660658 switch (ctrl->id) {
661659 case V4L2_CID_EXPOSURE:
662− /* 4 least significant bits of expsoure are fractional part */
663− ret = ov4689_write_reg(ov4689->client, OV4689_REG_EXPOSURE,
664− OV4689_REG_VALUE_24BIT, ctrl->val << 4);
660+ /* 4 least significant bits of exposure are fractional part */
661+ cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret);
665662 break;
666663 case V4L2_CID_ANALOGUE_GAIN:
667664 ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain);
668−
669− ret = ret ?:
670− ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_H,
671− OV4689_REG_VALUE_08BIT,
672− (sensor_gain >> OV4689_GAIN_H_SHIFT) &
673− OV4689_GAIN_H_MASK);
674− ret = ret ?:
675− ov4689_write_reg(ov4689->client, OV4689_REG_GAIN_L,
676− OV4689_REG_VALUE_08BIT,
677− sensor_gain & OV4689_GAIN_L_MASK);
665+ cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret);
678666 break;
679667 case V4L2_CID_VBLANK:
680− ret = ov4689_write_reg(ov4689->client, OV4689_REG_VTS,
681− OV4689_REG_VALUE_16BIT,
682− ctrl->val + ov4689->cur_mode->height);
668+ cci_write(regmap, OV4689_REG_VTS,
669+ ctrl->val + ov4689->cur_mode->height, &ret);
683670 break;
684671 case V4L2_CID_TEST_PATTERN:
685672 ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
686673 break;
674+ case V4L2_CID_HBLANK:
675+ cci_write(regmap, OV4689_REG_HTS,
676+ (ctrl->val + ov4689->cur_mode->width) /
677+ OV4689_HTS_DIVIDER, &ret);
678+ break;
679+ case V4L2_CID_VFLIP:
680+ cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT1,
681+ OV4689_TIMING_FLIP_MASK,
682+ ctrl->val ? OV4689_TIMING_FLIP_BOTH : 0, &ret);
683+ break;
684+ case V4L2_CID_HFLIP:
685+ cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT2,
686+ OV4689_TIMING_FLIP_MASK,
687+ ctrl->val ? 0 : OV4689_TIMING_FLIP_BOTH, &ret);
688+ break;
689+ case V4L2_CID_DIGITAL_GAIN:
690+ cci_write(regmap, OV4689_REG_DIG_GAIN, ctrl->val, &ret);
691+ break;
692+ case V4L2_CID_RED_BALANCE:
693+ cci_write(regmap, OV4689_REG_WB_GAIN_RED, ctrl->val, &ret);
694+ break;
695+ case V4L2_CID_BLUE_BALANCE:
696+ cci_write(regmap, OV4689_REG_WB_GAIN_BLUE, ctrl->val, &ret);
697+ break;
687698 default:
688− dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
699+ dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n",
689700 __func__, ctrl->id, ctrl->val);
690701 ret = -EINVAL;
691702 break;
692703 }
693704
694− pm_runtime_put(&client->dev);
705+ pm_runtime_mark_last_busy(dev);
706+ pm_runtime_put_autosuspend(dev);
695707
696708 return ret;
697709 }
@@ -707,16 +719,15 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 9 unmodified lines
707719 struct v4l2_ctrl_handler *handler;
708720 const struct ov4689_mode *mode;
709721 s64 exposure_max, vblank_def;
722+ s64 hblank_def, hblank_min;
710723 struct v4l2_ctrl *ctrl;
711− s64 h_blank_def;
712724 int ret;
713725
714726 handler = &ov4689->ctrl_handler;
715727 mode = ov4689->cur_mode;
716− ret = v4l2_ctrl_handler_init(handler, 10);
728+ ret = v4l2_ctrl_handler_init(handler, 15);
717729 if (ret)
718730 return ret;
719− handler->lock = &ov4689->mutex;
720731
721732 ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
722733 link_freq_menu_items);
@@ -726,11 +737,11 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 3 unmodified lines
726737 v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
727738 mode->pixel_rate, 1, mode->pixel_rate);
728739
729− h_blank_def = mode->hts_def - mode->width;
730− ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def,
731− h_blank_def, 1, h_blank_def);
732− if (ctrl)
733− ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
740+ hblank_def = mode->hts_def - mode->width;
741+ hblank_min = mode->hts_min - mode->width;
742+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK,
743+ hblank_min, OV4689_HTS_MAX - mode->width,
744+ OV4689_HTS_DIVIDER, hblank_def);
734745
735746 vblank_def = mode->vts_def - mode->height;
736747 v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
@@ -754,10 +765,24 @@ static int ov4689_initialize_controls(struct ov4689 *ov4689) · 17 unmodified lines
754765 ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
755766 0, 0, ov4689_test_pattern_menu);
756767
768+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
769+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
770+
771+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
772+ OV4689_DIG_GAIN_MIN, OV4689_DIG_GAIN_MAX,
773+ OV4689_DIG_GAIN_STEP, OV4689_DIG_GAIN_DEFAULT);
774+
775+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_RED_BALANCE,
776+ OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
777+ OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
778+
779+ v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_BLUE_BALANCE,
780+ OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
781+ OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
782+
757783 if (handler->error) {
758784 ret = handler->error;
759− dev_err(&ov4689->client->dev, "Failed to init controls(%d)\n",
760− ret);
785+ dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret);
761786 goto err_free_handler;
762787 }
763788
@@ -783,19 +808,18 @@ err_free_handler: · 19 unmodified lines
783808 static int ov4689_check_sensor_id(struct ov4689 *ov4689,
784809 struct i2c_client *client)
785810 {
786− struct device *dev = &ov4689->client->dev;
787− u32 id = 0;
811+ struct device *dev = ov4689->dev;
812+ u64 id = 0;
788813 int ret;
789814
790− ret = ov4689_read_reg(client, OV4689_REG_CHIP_ID,
791− OV4689_REG_VALUE_16BIT, &id);
815+ ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL);
792816 if (ret) {
793817 dev_err(dev, "Cannot read sensor ID\n");
794818 return ret;
795819 }
796820
797821 if (id != CHIP_ID) {
798− dev_err(dev, "Unexpected sensor ID %06x, expected %06x\n",
822+ dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n",
799823 id, CHIP_ID);
800824 return -ENODEV;
801825 }
@@ -812,7 +836,7 @@ static int ov4689_configure_regulators(struct ov4689 *ov4689) · 10 unmodified lines
812836 for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++)
813837 ov4689->supplies[i].supply = ov4689_supply_names[i];
814838
815− return devm_regulator_bulk_get(&ov4689->client->dev,
839+ return devm_regulator_bulk_get(ov4689->dev,
816840 ARRAY_SIZE(ov4689_supply_names),
817841 ov4689->supplies);
818842 }
@@ -881,7 +905,8 @@ static int ov4689_probe(struct i2c_client *client) · 62 unmodified lines
881905 if (!ov4689)
882906 return -ENOMEM;
883907
884− ov4689->client = client;
908+ ov4689->dev = dev;
909+
885910 ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];
886911
887912 ov4689->xvclk = devm_clk_get_optional(dev, NULL);
@@ -905,6 +930,13 @@ static int ov4689_probe(struct i2c_client *client) · 17 unmodified lines
905930 return -EINVAL;
906931 }
907932
933+ ov4689->regmap = devm_cci_regmap_init_i2c(client, 16);
934+ if (IS_ERR(ov4689->regmap)) {
935+ ret = PTR_ERR(ov4689->regmap);
936+ dev_err(dev, "failed to initialize CCI: %d\n", ret);
937+ return ret;
938+ }
939+
908940 ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset",
909941 GPIOD_OUT_LOW);
910942 if (IS_ERR(ov4689->reset_gpio)) {
@@ -923,13 +955,15 @@ static int ov4689_probe(struct i2c_client *client) · 12 unmodified lines
923955 return dev_err_probe(dev, ret,
924956 "Failed to get power regulators\n");
925957
926− mutex_init(&ov4689->mutex);
927−
928958 sd = &ov4689->subdev;
929959 v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
960+ sd->internal_ops = &ov4689_internal_ops;
961+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
930962 ret = ov4689_initialize_controls(ov4689);
931− if (ret)
932− goto err_destroy_mutex;
963+ if (ret) {
964+ dev_err(dev, "Failed to initialize controls\n");
965+ return ret;
966+ }
933967
934968 ret = ov4689_power_on(dev);
935969 if (ret)
@@ -939,35 +973,47 @@ static int ov4689_probe(struct i2c_client *client) · 3 unmodified lines
939973 if (ret)
940974 goto err_power_off;
941975
942− sd->internal_ops = &ov4689_internal_ops;
943− sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
944976
945− ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
946977 sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
978+ ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
947979 ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
948980 if (ret < 0)
949981 goto err_power_off;
950982
951− ret = v4l2_async_register_subdev_sensor(sd);
983+ sd->state_lock = ov4689->ctrl_handler.lock;
984+ ret = v4l2_subdev_init_finalize(sd);
952985 if (ret) {
953− dev_err(dev, "v4l2 async register subdev failed\n");
986+ dev_err(dev, "Could not register v4l2 device\n");
954987 goto err_clean_entity;
955988 }
956989
957990 pm_runtime_set_active(dev);
991+ pm_runtime_get_noresume(dev);
958992 pm_runtime_enable(dev);
959− pm_runtime_idle(dev);
993+ pm_runtime_set_autosuspend_delay(dev, 1000);
994+ pm_runtime_use_autosuspend(dev);
995+
996+ ret = v4l2_async_register_subdev_sensor(sd);
997+ if (ret) {
998+ dev_err(dev, "v4l2 async register subdev failed\n");
999+ goto err_clean_subdev_pm;
1000+ }
1001+
1002+ pm_runtime_mark_last_busy(dev);
1003+ pm_runtime_put_autosuspend(dev);
9601004
9611005 return 0;
9621006
1007+err_clean_subdev_pm:
1008+ pm_runtime_disable(dev);
1009+ pm_runtime_put_noidle(dev);
1010+ v4l2_subdev_cleanup(sd);
9631011 err_clean_entity:
9641012 media_entity_cleanup(&sd->entity);
9651013 err_power_off:
9661014 ov4689_power_off(dev);
9671015 err_free_handler:
9681016 v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
969−err_destroy_mutex:
970− mutex_destroy(&ov4689->mutex);
9711017
9721018 return ret;
9731019 }
@@ -979,9 +1025,8 @@ static void ov4689_remove(struct i2c_client *client) · 5 unmodified lines
9791025
9801026 v4l2_async_unregister_subdev(sd);
9811027 media_entity_cleanup(&sd->entity);
982−
1028+ v4l2_subdev_cleanup(sd);
9831029 v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
984− mutex_destroy(&ov4689->mutex);
9851030
9861031 pm_runtime_disable(&client->dev);
9871032 if (!pm_runtime_status_suspended(&client->dev))
drivers/media/i2c/rdacm20.c+2−2
@@ -463,8 +463,8 @@ static int rdacm20_initialize(struct rdacm20_device *dev) · 462 unmodified lines
463463 return ret;
464464
465465 /*
466− * Ensure that we have a good link configuration before attempting to
467− * identify the device.
466+ * Ensure that we have a good link configuration before attempting to
467+ * identify the device.
468468 */
469469 ret = max9271_configure_i2c(&dev->serializer,
470470 MAX9271_I2CSLVSH_469NS_234NS |
drivers/media/i2c/st-mipid02.c+1−1
@@ -326,7 +326,7 @@ static int mipid02_configure_from_rx_speed(struct mipid02_dev *bridge, · 325 unmodified lines
326326 }
327327
328328 dev_dbg(&client->dev, "detect link_freq = %lld Hz", link_freq);
329− do_div(ui_4, link_freq);
329+ ui_4 = div64_u64(ui_4, link_freq);
330330 bridge->r.clk_lane_reg1 |= ui_4 << 2;
331331
332332 return 0;
drivers/media/i2c/tc358743.c+17−8
@@ -1521,11 +1521,14 @@ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1520 unmodified lines
15211521 return 0;
15221522 }
15231523
1524−static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
1524+static int tc358743_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
15251525 struct v4l2_dv_timings *timings)
15261526 {
15271527 struct tc358743_state *state = to_state(sd);
15281528
1529+ if (pad != 0)
1530+ return -EINVAL;
1531+
15291532 if (!timings)
15301533 return -EINVAL;
15311534
@@ -1553,11 +1556,14 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd, · 21 unmodified lines
15531556 return 0;
15541557 }
15551558
1556−static int tc358743_g_dv_timings(struct v4l2_subdev *sd,
1559+static int tc358743_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
15571560 struct v4l2_dv_timings *timings)
15581561 {
15591562 struct tc358743_state *state = to_state(sd);
15601563
1564+ if (pad != 0)
1565+ return -EINVAL;
1566+
15611567 *timings = state->timings;
15621568
15631569 return 0;
@@ -1573,11 +1579,14 @@ static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, · 9 unmodified lines
15731579 &tc358743_timings_cap, NULL, NULL);
15741580 }
15751581
1576−static int tc358743_query_dv_timings(struct v4l2_subdev *sd,
1577− struct v4l2_dv_timings *timings)
1582+static int tc358743_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
1583+ struct v4l2_dv_timings *timings)
15781584 {
15791585 int ret;
15801586
1587+ if (pad != 0)
1588+ return -EINVAL;
1589+
15811590 ret = tc358743_get_detected_timings(sd, timings);
15821591 if (ret)
15831592 return ret;
@@ -1822,9 +1831,6 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = { · 238 unmodified lines
18221831
18231832 static const struct v4l2_subdev_video_ops tc358743_video_ops = {
18241833 .g_input_status = tc358743_g_input_status,
1825− .s_dv_timings = tc358743_s_dv_timings,
1826− .g_dv_timings = tc358743_g_dv_timings,
1827− .query_dv_timings = tc358743_query_dv_timings,
18281834 .s_stream = tc358743_s_stream,
18291835 };
18301836
@@ -1834,6 +1840,9 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { · 3 unmodified lines
18341840 .get_fmt = tc358743_get_fmt,
18351841 .get_edid = tc358743_g_edid,
18361842 .set_edid = tc358743_s_edid,
1843+ .s_dv_timings = tc358743_s_dv_timings,
1844+ .g_dv_timings = tc358743_g_dv_timings,
1845+ .query_dv_timings = tc358743_query_dv_timings,
18371846 .enum_dv_timings = tc358743_enum_dv_timings,
18381847 .dv_timings_cap = tc358743_dv_timings_cap,
18391848 .get_mbus_config = tc358743_get_mbus_config,
@@ -2110,7 +2119,7 @@ static int tc358743_probe(struct i2c_client *client) · 270 unmodified lines
21102119
21112120 tc358743_initial_setup(sd);
21122121
2113− tc358743_s_dv_timings(sd, &default_timing);
2122+ tc358743_s_dv_timings(sd, 0, &default_timing);
21142123
21152124 tc358743_set_csi_color_space(sd);
21162125
drivers/media/i2c/tc358746.c+1−2
@@ -844,8 +844,7 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, · 843 unmodified lines
844844 continue;
845845
846846 tmp = fout * postdiv;
847− do_div(tmp, fin);
848− mul = tmp;
847+ mul = div64_ul(tmp, fin);
849848 if (mul > 511)
850849 continue;
851850
drivers/media/i2c/tda1997x.c+7−7
@@ -1669,8 +1669,8 @@ tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status) · 1668 unmodified lines
16691669 return 0;
16701670 };
16711671
1672−static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
1673− struct v4l2_dv_timings *timings)
1672+static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
1673+ struct v4l2_dv_timings *timings)
16741674 {
16751675 struct tda1997x_state *state = to_state(sd);
16761676
@@ -1694,7 +1694,7 @@ static int tda1997x_s_dv_timings(struct v4l2_subdev *sd, · 17 unmodified lines
16941694 return 0;
16951695 }
16961696
1697−static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
1697+static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
16981698 struct v4l2_dv_timings *timings)
16991699 {
17001700 struct tda1997x_state *state = to_state(sd);
@@ -1707,7 +1707,7 @@ static int tda1997x_g_dv_timings(struct v4l2_subdev *sd, · 6 unmodified lines
17071707 return 0;
17081708 }
17091709
1710−static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
1710+static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
17111711 struct v4l2_dv_timings *timings)
17121712 {
17131713 struct tda1997x_state *state = to_state(sd);
@@ -1724,9 +1724,6 @@ static int tda1997x_query_dv_timings(struct v4l2_subdev *sd, · 10 unmodified lines
17241724
17251725 static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
17261726 .g_input_status = tda1997x_g_input_status,
1727− .s_dv_timings = tda1997x_s_dv_timings,
1728− .g_dv_timings = tda1997x_g_dv_timings,
1729− .query_dv_timings = tda1997x_query_dv_timings,
17301727 };
17311728
17321729
@@ -1930,6 +1927,9 @@ static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = { · 197 unmodified lines
19301927 .set_fmt = tda1997x_set_format,
19311928 .get_edid = tda1997x_get_edid,
19321929 .set_edid = tda1997x_set_edid,
1930+ .s_dv_timings = tda1997x_s_dv_timings,
1931+ .g_dv_timings = tda1997x_g_dv_timings,
1932+ .query_dv_timings = tda1997x_query_dv_timings,
19331933 .dv_timings_cap = tda1997x_get_dv_timings_cap,
19341934 .enum_dv_timings = tda1997x_enum_dv_timings,
19351935 };
drivers/media/i2c/ths7303.c+7−3
@@ -193,8 +193,8 @@ static int ths7303_s_stream(struct v4l2_subdev *sd, int enable) · 192 unmodified lines
193193 }
194194
195195 /* for setting filter for HD output */
196−static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
197− struct v4l2_dv_timings *dv_timings)
196+static int ths7303_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
197+ struct v4l2_dv_timings *dv_timings)
198198 {
199199 struct ths7303_state *state = to_state(sd);
200200
@@ -210,7 +210,6 @@ static int ths7303_s_dv_timings(struct v4l2_subdev *sd, · 9 unmodified lines
210210 static const struct v4l2_subdev_video_ops ths7303_video_ops = {
211211 .s_stream = ths7303_s_stream,
212212 .s_std_output = ths7303_s_std_output,
213− .s_dv_timings = ths7303_s_dv_timings,
214213 };
215214
216215 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -317,9 +316,14 @@ static const struct v4l2_subdev_core_ops ths7303_core_ops = { · 100 unmodified lines
317316 #endif
318317 };
319318
319+static const struct v4l2_subdev_pad_ops ths7303_pad_ops = {
320+ .s_dv_timings = ths7303_s_dv_timings,
321+};
322+
320323 static const struct v4l2_subdev_ops ths7303_ops = {
321324 .core = &ths7303_core_ops,
322325 .video = &ths7303_video_ops,
326+ .pad = &ths7303_pad_ops,
323327 };
324328
325329 static int ths7303_probe(struct i2c_client *client)
drivers/media/i2c/ths8200.c+10−4
@@ -358,13 +358,16 @@ static void ths8200_setup(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt) · 357 unmodified lines
358358 bt->hsync, bt->vsync);
359359 }
360360
361−static int ths8200_s_dv_timings(struct v4l2_subdev *sd,
361+static int ths8200_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
362362 struct v4l2_dv_timings *timings)
363363 {
364364 struct ths8200_state *state = to_state(sd);
365365
366366 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
367367
368+ if (pad != 0)
369+ return -EINVAL;
370+
368371 if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap,
369372 NULL, NULL))
370373 return -EINVAL;
@@ -385,13 +388,16 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, · 14 unmodified lines
385388 return 0;
386389 }
387390
388−static int ths8200_g_dv_timings(struct v4l2_subdev *sd,
391+static int ths8200_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
389392 struct v4l2_dv_timings *timings)
390393 {
391394 struct ths8200_state *state = to_state(sd);
392395
393396 v4l2_dbg(1, debug, sd, "%s:\n", __func__);
394397
398+ if (pad != 0)
399+ return -EINVAL;
400+
395401 *timings = state->dv_timings;
396402
397403 return 0;
@@ -420,11 +426,11 @@ static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, · 22 unmodified lines
420426 /* Specific video subsystem operation handlers */
421427 static const struct v4l2_subdev_video_ops ths8200_video_ops = {
422428 .s_stream = ths8200_s_stream,
423− .s_dv_timings = ths8200_s_dv_timings,
424− .g_dv_timings = ths8200_g_dv_timings,
425429 };
426430
427431 static const struct v4l2_subdev_pad_ops ths8200_pad_ops = {
432+ .s_dv_timings = ths8200_s_dv_timings,
433+ .g_dv_timings = ths8200_g_dv_timings,
428434 .enum_dv_timings = ths8200_enum_dv_timings,
429435 .dv_timings_cap = ths8200_dv_timings_cap,
430436 };
drivers/media/i2c/tvp7002.c+21−11
@@ -546,13 +546,16 @@ static int tvp7002_write_inittab(struct v4l2_subdev *sd, · 545 unmodified lines
546546 return error;
547547 }
548548
549−static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
550− struct v4l2_dv_timings *dv_timings)
549+static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
550+ struct v4l2_dv_timings *dv_timings)
551551 {
552552 struct tvp7002 *device = to_tvp7002(sd);
553553 const struct v4l2_bt_timings *bt = &dv_timings->bt;
554554 int i;
555555
556+ if (pad != 0)
557+ return -EINVAL;
558+
556559 if (dv_timings->type != V4L2_DV_BT_656_1120)
557560 return -EINVAL;
558561 for (i = 0; i < NUM_TIMINGS; i++) {
@@ -566,11 +569,14 @@ static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, · 7 unmodified lines
566569 return -EINVAL;
567570 }
568571
569−static int tvp7002_g_dv_timings(struct v4l2_subdev *sd,
570− struct v4l2_dv_timings *dv_timings)
572+static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
573+ struct v4l2_dv_timings *dv_timings)
571574 {
572575 struct tvp7002 *device = to_tvp7002(sd);
573576
577+ if (pad != 0)
578+ return -EINVAL;
579+
574580 *dv_timings = device->current_timings->timings;
575581 return 0;
576582 }
@@ -659,12 +665,16 @@ static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) · 82 unmodified lines
659665 return 0;
660666 }
661667
662−static int tvp7002_query_dv_timings(struct v4l2_subdev *sd,
663− struct v4l2_dv_timings *timings)
668+static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
669+ struct v4l2_dv_timings *timings)
664670 {
665671 int index;
666− int err = tvp7002_query_dv(sd, &index);
672+ int err;
673+
674+ if (pad != 0)
675+ return -EINVAL;
667676
677+ err = tvp7002_query_dv(sd, &index);
668678 if (err)
669679 return err;
670680 *timings = tvp7002_timings[index].timings;
@@ -861,9 +871,6 @@ static const struct v4l2_subdev_core_ops tvp7002_core_ops = { · 190 unmodified lines
861871
862872 /* Specific video subsystem operation handlers */
863873 static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
864− .g_dv_timings = tvp7002_g_dv_timings,
865− .s_dv_timings = tvp7002_s_dv_timings,
866− .query_dv_timings = tvp7002_query_dv_timings,
867874 .s_stream = tvp7002_s_stream,
868875 };
869876
@@ -872,6 +879,9 @@ static const struct v4l2_subdev_pad_ops tvp7002_pad_ops = { · 2 unmodified lines
872879 .enum_mbus_code = tvp7002_enum_mbus_code,
873880 .get_fmt = tvp7002_get_pad_format,
874881 .set_fmt = tvp7002_set_pad_format,
882+ .g_dv_timings = tvp7002_g_dv_timings,
883+ .s_dv_timings = tvp7002_s_dv_timings,
884+ .query_dv_timings = tvp7002_query_dv_timings,
875885 .enum_dv_timings = tvp7002_enum_dv_timings,
876886 };
877887
@@ -1001,7 +1011,7 @@ static int tvp7002_probe(struct i2c_client *c) · 123 unmodified lines
10011011
10021012 /* Set registers according to default video mode */
10031013 timings = device->current_timings->timings;
1004− error = tvp7002_s_dv_timings(sd, &timings);
1014+ error = tvp7002_s_dv_timings(sd, 0, &timings);
10051015
10061016 #if defined(CONFIG_MEDIA_CONTROLLER)
10071017 device->pad.flags = MEDIA_PAD_FL_SOURCE;
drivers/media/mc/mc-devnode.c+2−3
@@ -245,15 +245,14 @@ int __must_check media_devnode_register(struct media_device *mdev, · 244 unmodified lines
245245 kobject_set_name(&devnode->cdev.kobj, "media%d", devnode->minor);
246246
247247 /* Part 3: Add the media and char device */
248+ set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
248249 ret = cdev_device_add(&devnode->cdev, &devnode->dev);
249250 if (ret < 0) {
251+ clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
250252 pr_err("%s: cdev_device_add failed\n", __func__);
251253 goto cdev_add_error;
252254 }
253255
254− /* Part 4: Activate this minor. The char device can now be used. */
255− set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
256−
257256 return 0;
258257
259258 cdev_add_error:
drivers/media/mc/mc-entity.c+6−0
@@ -619,6 +619,12 @@ static int media_pipeline_explore_next_link(struct media_pipeline *pipe, · 618 unmodified lines
619619 link = list_entry(entry->links, typeof(*link), list);
620620 last_link = media_pipeline_walk_pop(walk);
621621
622+ if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK) {
623+ dev_dbg(walk->mdev->dev,
624+ "media pipeline: skipping link (not data-link)\n");
625+ return 0;
626+ }
627+
622628 dev_dbg(walk->mdev->dev,
623629 "media pipeline: exploring link '%s':%u -> '%s':%u\n",
624630 link->source->entity->name, link->source->index,
drivers/media/mmc/siano/smssdio.c+1−24
@@ -344,30 +344,7 @@ static struct sdio_driver smssdio_driver = { · 343 unmodified lines
344344 .probe = smssdio_probe,
345345 .remove = smssdio_remove,
346346 };
347−
348−/*******************************************************************/
349−/* Module functions */
350−/*******************************************************************/
351−
352−static int __init smssdio_module_init(void)
353−{
354− int ret = 0;
355−
356− printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
357− printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
358−
359− ret = sdio_register_driver(&smssdio_driver);
360−
361− return ret;
362−}
363−
364−static void __exit smssdio_module_exit(void)
365−{
366− sdio_unregister_driver(&smssdio_driver);
367−}
368−
369−module_init(smssdio_module_init);
370−module_exit(smssdio_module_exit);
347+module_sdio_driver(smssdio_driver);
371348
372349 MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
373350 MODULE_AUTHOR("Pierre Ossman");
drivers/media/pci/cobalt/cobalt-v4l2.c+6−6
@@ -633,7 +633,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh, · 632 unmodified lines
633633 return -EBUSY;
634634
635635 err = v4l2_subdev_call(s->sd,
636− video, s_dv_timings, timings);
636+ pad, s_dv_timings, 0, timings);
637637 if (!err) {
638638 s->timings = *timings;
639639 s->width = timings->bt.width;
@@ -653,7 +653,7 @@ static int cobalt_g_dv_timings(struct file *file, void *priv_fh, · 13 unmodified lines
653653 return 0;
654654 }
655655 return v4l2_subdev_call(s->sd,
656− video, g_dv_timings, timings);
656+ pad, g_dv_timings, 0, timings);
657657 }
658658
659659 static int cobalt_query_dv_timings(struct file *file, void *priv_fh,
@@ -666,7 +666,7 @@ static int cobalt_query_dv_timings(struct file *file, void *priv_fh, · 6 unmodified lines
666666 return 0;
667667 }
668668 return v4l2_subdev_call(s->sd,
669− video, query_dv_timings, timings);
669+ pad, query_dv_timings, 0, timings);
670670 }
671671
672672 static int cobalt_dv_timings_cap(struct file *file, void *priv_fh,
@@ -1080,7 +1080,7 @@ static int cobalt_g_pixelaspect(struct file *file, void *fh, · 407 unmodified lines
10801080 if (s->input == 1)
10811081 timings = cea1080p60;
10821082 else
1083− err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
1083+ err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
10841084 if (!err)
10851085 *f = v4l2_dv_timings_aspect_ratio(&timings);
10861086 return err;
@@ -1099,7 +1099,7 @@ static int cobalt_g_selection(struct file *file, void *fh, · 12 unmodified lines
10991099 if (s->input == 1)
11001100 timings = cea1080p60;
11011101 else
1102− err = v4l2_subdev_call(s->sd, video, g_dv_timings, &timings);
1102+ err = v4l2_subdev_call(s->sd, pad, g_dv_timings, 0, &timings);
11031103
11041104 if (err)
11051105 return err;
@@ -1243,7 +1243,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) · 137 unmodified lines
12431243 if (s->sd)
12441244 vdev->ctrl_handler = s->sd->ctrl_handler;
12451245 s->timings = dv1080p60;
1246− v4l2_subdev_call(s->sd, video, s_dv_timings, &s->timings);
1246+ v4l2_subdev_call(s->sd, pad, s_dv_timings, 0, &s->timings);
12471247 if (!s->is_output && s->sd)
12481248 cobalt_enable_input(s);
12491249 vdev->ioctl_ops = s->is_dummy ? &cobalt_ioctl_empty_ops :
drivers/media/pci/intel/Kconfig+3−1
@@ -1,11 +1,13 @@
11 # SPDX-License-Identifier: GPL-2.0-only
22
33 source "drivers/media/pci/intel/ipu3/Kconfig"
4+source "drivers/media/pci/intel/ipu6/Kconfig"
45 source "drivers/media/pci/intel/ivsc/Kconfig"
56
67 config IPU_BRIDGE
78 tristate "Intel IPU Bridge"
8− depends on I2C && ACPI
9+ depends on ACPI || COMPILE_TEST
10+ depends on I2C
911 help
1012 The IPU bridge is a helper library for Intel IPU drivers to
1113 function on systems shipped with Windows.
drivers/media/pci/intel/Makefile+1−0
@@ -5,3 +5,4 @@ · 4 unmodified lines
55 obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o
66 obj-y += ipu3/
77 obj-y += ivsc/
8+obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/
drivers/media/pci/intel/ipu-bridge.c+47−19
@@ -15,6 +15,8 @@ · 14 unmodified lines
1515 #include <media/ipu-bridge.h>
1616 #include <media/v4l2-fwnode.h>
1717
18+#define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev))
19+
1820 /*
1921 * 92335fcf-3203-4472-af93-7b4453ac29da
2022 *
@@ -87,6 +89,7 @@ static const char * const ipu_vcm_types[] = { · 66 unmodified lines
8789 "lc898212axb",
8890 };
8991
92+#if IS_ENABLED(CONFIG_ACPI)
9093 /*
9194 * Used to figure out IVSC acpi device by ipu_bridge_get_ivsc_acpi_dev()
9295 * instead of device and driver match to probe IVSC device.
@@ -100,13 +103,13 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { · 7 unmodified lines
100103
101104 static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
102105 {
103− acpi_handle handle = acpi_device_handle(adev);
104− struct acpi_device *consumer, *ivsc_adev;
105106 unsigned int i;
106107
107108 for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) {
108109 const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i];
110+ struct acpi_device *consumer, *ivsc_adev;
109111
112+ acpi_handle handle = acpi_device_handle(adev);
110113 for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1)
111114 /* camera sensor depends on IVSC in DSDT if exist */
112115 for_each_acpi_consumer_dev(ivsc_adev, consumer)
@@ -118,6 +121,12 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev · 5 unmodified lines
118121
119122 return NULL;
120123 }
124+#else
125+static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
126+{
127+ return NULL;
128+}
129+#endif
121130
122131 static int ipu_bridge_match_ivsc_dev(struct device *dev, const void *adev)
123132 {
@@ -163,7 +172,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor, · 39 unmodified lines
163172 csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
164173 if (!csi_dev) {
165174 acpi_dev_put(adev);
166− dev_err(&adev->dev, "Failed to find MEI CSI dev\n");
175+ dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n");
167176 return -ENODEV;
168177 }
169178
@@ -182,24 +191,25 @@ static int ipu_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, · 12 unmodified lines
182191 acpi_status status;
183192 int ret = 0;
184193
185− status = acpi_evaluate_object(adev->handle, id, NULL, &buffer);
194+ status = acpi_evaluate_object(ACPI_PTR(adev->handle),
195+ id, NULL, &buffer);
186196 if (ACPI_FAILURE(status))
187197 return -ENODEV;
188198
189199 obj = buffer.pointer;
190200 if (!obj) {
191− dev_err(&adev->dev, "Couldn't locate ACPI buffer\n");
201+ dev_err(ADEV_DEV(adev), "Couldn't locate ACPI buffer\n");
192202 return -ENODEV;
193203 }
194204
195205 if (obj->type != ACPI_TYPE_BUFFER) {
196− dev_err(&adev->dev, "Not an ACPI buffer\n");
206+ dev_err(ADEV_DEV(adev), "Not an ACPI buffer\n");
197207 ret = -ENODEV;
198208 goto out_free_buff;
199209 }
200210
201211 if (obj->buffer.length > size) {
202− dev_err(&adev->dev, "Given buffer is too small\n");
212+ dev_err(ADEV_DEV(adev), "Given buffer is too small\n");
203213 ret = -EINVAL;
204214 goto out_free_buff;
205215 }
@@ -220,7 +230,7 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, · 14 unmodified lines
220230 case IPU_SENSOR_ROTATION_INVERTED:
221231 return 180;
222232 default:
223− dev_warn(&adev->dev,
233+ dev_warn(ADEV_DEV(adev),
224234 "Unknown rotation %d. Assume 0 degree rotation\n",
225235 ssdb->degree);
226236 return 0;
@@ -230,12 +240,14 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, · 3 unmodified lines
230240 static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_device *adev)
231241 {
232242 enum v4l2_fwnode_orientation orientation;
233− struct acpi_pld_info *pld;
234− acpi_status status;
243+ struct acpi_pld_info *pld = NULL;
244+ acpi_status status = AE_ERROR;
235245
246+#if IS_ENABLED(CONFIG_ACPI)
236247 status = acpi_get_physical_device_location(adev->handle, &pld);
248+#endif
237249 if (ACPI_FAILURE(status)) {
238− dev_warn(&adev->dev, "_PLD call failed, using default orientation\n");
250+ dev_warn(ADEV_DEV(adev), "_PLD call failed, using default orientation\n");
239251 return V4L2_FWNODE_ORIENTATION_EXTERNAL;
240252 }
241253
@@ -253,7 +265,8 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev · 11 unmodified lines
253265 orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
254266 break;
255267 default:
256− dev_warn(&adev->dev, "Unknown _PLD panel val %d\n", pld->panel);
268+ dev_warn(ADEV_DEV(adev), "Unknown _PLD panel val %d\n",
269+ pld->panel);
257270 orientation = V4L2_FWNODE_ORIENTATION_EXTERNAL;
258271 break;
259272 }
@@ -272,12 +285,12 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) · 12 unmodified lines
272285 return ret;
273286
274287 if (ssdb.vcmtype > ARRAY_SIZE(ipu_vcm_types)) {
275− dev_warn(&adev->dev, "Unknown VCM type %d\n", ssdb.vcmtype);
288+ dev_warn(ADEV_DEV(adev), "Unknown VCM type %d\n", ssdb.vcmtype);
276289 ssdb.vcmtype = 0;
277290 }
278291
279292 if (ssdb.lanes > IPU_MAX_LANES) {
280− dev_err(&adev->dev, "Number of lanes in SSDB is invalid\n");
293+ dev_err(ADEV_DEV(adev), "Number of lanes in SSDB is invalid\n");
281294 return -EINVAL;
282295 }
283296
@@ -465,8 +478,14 @@ static void ipu_bridge_create_connection_swnodes(struct ipu_bridge *bridge, · 181 unmodified lines
465478 sensor->ipu_properties);
466479
467480 if (sensor->csi_dev) {
481+ const char *device_hid = "";
482+
483+#if IS_ENABLED(CONFIG_ACPI)
484+ device_hid = acpi_device_hid(sensor->ivsc_adev);
485+#endif
486+
468487 snprintf(sensor->ivsc_name, sizeof(sensor->ivsc_name), "%s-%u",
469− acpi_device_hid(sensor->ivsc_adev), sensor->link);
488+ device_hid, sensor->link);
470489
471490 nodes[SWNODE_IVSC_HID] = NODE_SENSOR(sensor->ivsc_name,
472491 sensor->ivsc_properties);
@@ -631,11 +650,15 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, · 158 unmodified lines
631650 {
632651 struct fwnode_handle *fwnode, *primary;
633652 struct ipu_sensor *sensor;
634− struct acpi_device *adev;
653+ struct acpi_device *adev = NULL;
635654 int ret;
636655
656+#if IS_ENABLED(CONFIG_ACPI)
637657 for_each_acpi_dev_match(adev, cfg->hid, NULL, -1) {
638− if (!adev->status.enabled)
658+#else
659+ while (true) {
660+#endif
661+ if (!ACPI_PTR(adev->status.enabled))
639662 continue;
640663
641664 if (bridge->n_sensors >= IPU_MAX_PORTS) {
@@ -671,7 +694,7 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg, · 29 unmodified lines
671694 goto err_free_swnodes;
672695 }
673696
674− sensor->adev = acpi_dev_get(adev);
697+ sensor->adev = ACPI_PTR(acpi_dev_get(adev));
675698
676699 primary = acpi_fwnode_handle(adev);
677700 primary->secondary = fwnode;
@@ -727,11 +750,16 @@ static int ipu_bridge_ivsc_is_ready(void) · 49 unmodified lines
727750 unsigned int i;
728751
729752 for (i = 0; i < ARRAY_SIZE(ipu_supported_sensors); i++) {
753+#if IS_ENABLED(CONFIG_ACPI)
730754 const struct ipu_sensor_config *cfg =
731755 &ipu_supported_sensors[i];
732756
733757 for_each_acpi_dev_match(sensor_adev, cfg->hid, NULL, -1) {
734− if (!sensor_adev->status.enabled)
758+#else
759+ while (true) {
760+ sensor_adev = NULL;
761+#endif
762+ if (!ACPI_PTR(sensor_adev->status.enabled))
735763 continue;
736764
737765 adev = ipu_bridge_get_ivsc_acpi_dev(sensor_adev);
drivers/media/pci/intel/ipu3/ipu3-cio2.c+9−20
@@ -4,9 +4,9 @@ · 3 unmodified lines
44 *
55 * Based partially on Intel IPU4 driver written by
66 * Sakari Ailus <sakari.ailus@linux.intel.com>
7− * Samu Onkalo <samu.onkalo@intel.com>
7+ * Samu Onkalo
88 * Jouni Högander <jouni.hogander@intel.com>
9− * Jouni Ukkonen <jouni.ukkonen@intel.com>
9+ * Jouni Ukkonen
1010 * Antti Laakso <antti.laakso@intel.com>
1111 * et al.
1212 */
@@ -1752,11 +1752,6 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, · 1739 unmodified lines
17521752
17531753 v4l2_async_nf_init(&cio2->notifier, &cio2->v4l2_dev);
17541754
1755− /* Register notifier for subdevices we care */
1756− r = cio2_parse_firmware(cio2);
1757− if (r)
1758− goto fail_clean_notifier;
1759−
17601755 r = devm_request_irq(dev, pci_dev->irq, cio2_irq, IRQF_SHARED,
17611756 CIO2_NAME, cio2);
17621757 if (r) {
@@ -1764,6 +1759,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, · 1 unmodified line
17641759 goto fail_clean_notifier;
17651760 }
17661761
1762+ /* Register notifier for subdevices we care */
1763+ r = cio2_parse_firmware(cio2);
1764+ if (r)
1765+ goto fail_clean_notifier;
1766+
17671767 pm_runtime_put_noidle(dev);
17681768 pm_runtime_allow(dev);
17691769
@@ -1807,16 +1807,10 @@ static int __maybe_unused cio2_runtime_suspend(struct device *dev) · 37 unmodified lines
18071807 struct pci_dev *pci_dev = to_pci_dev(dev);
18081808 struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
18091809 void __iomem *const base = cio2->base;
1810− u16 pm;
18111810
18121811 writel(CIO2_D0I3C_I3, base + CIO2_REG_D0I3C);
18131812 dev_dbg(dev, "cio2 runtime suspend.\n");
18141813
1815− pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
1816− pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
1817− pm |= CIO2_PMCSR_D3;
1818− pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
1819−
18201814 return 0;
18211815 }
18221816
@@ -1825,15 +1819,10 @@ static int __maybe_unused cio2_runtime_resume(struct device *dev) · 2 unmodified lines
18251819 struct pci_dev *pci_dev = to_pci_dev(dev);
18261820 struct cio2_device *cio2 = pci_get_drvdata(pci_dev);
18271821 void __iomem *const base = cio2->base;
1828− u16 pm;
18291822
18301823 writel(CIO2_D0I3C_RR, base + CIO2_REG_D0I3C);
18311824 dev_dbg(dev, "cio2 runtime resume.\n");
18321825
1833− pci_read_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, &pm);
1834− pm = (pm >> CIO2_PMCSR_D0D3_SHIFT) << CIO2_PMCSR_D0D3_SHIFT;
1835− pci_write_config_word(pci_dev, pci_dev->pm_cap + CIO2_PMCSR_OFFSET, pm);
1836−
18371826 return 0;
18381827 }
18391828
@@ -2006,10 +1995,10 @@ static struct pci_driver cio2_pci_driver = { · 166 unmodified lines
20061995
20071996 module_pci_driver(cio2_pci_driver);
20081997
2009−MODULE_AUTHOR("Tuukka Toivonen <tuukka.toivonen@intel.com>");
1998+MODULE_AUTHOR("Tuukka Toivonen");
20101999 MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
20112000 MODULE_AUTHOR("Jian Xu Zheng");
2012−MODULE_AUTHOR("Yuning Pu <yuning.pu@intel.com>");
2001+MODULE_AUTHOR("Yuning Pu");
20132002 MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
20142003 MODULE_LICENSE("GPL v2");
20152004 MODULE_DESCRIPTION("IPU3 CIO2 driver");
drivers/media/pci/intel/ipu3/ipu3-cio2.h+0−4
@@ -320,10 +320,6 @@ struct pci_dev; · 319 unmodified lines
320320 #define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4
321321 #define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570
322322
323−#define CIO2_PMCSR_OFFSET 4U
324−#define CIO2_PMCSR_D0D3_SHIFT 2U
325−#define CIO2_PMCSR_D3 0x3
326−
327323 struct cio2_csi2_timing {
328324 s32 clk_termen;
329325 s32 clk_settle;
drivers/media/pci/intel/ipu6/Kconfig+18−0
@@ -0,0 +1,18 @@
1+config VIDEO_INTEL_IPU6
2+ tristate "Intel IPU6 driver"
3+ depends on ACPI || COMPILE_TEST
4+ depends on VIDEO_DEV
5+ depends on X86 && X86_64 && HAS_DMA
6+ select DMA_OPS
7+ select IOMMU_IOVA
8+ select VIDEO_V4L2_SUBDEV_API
9+ select MEDIA_CONTROLLER
10+ select VIDEOBUF2_DMA_CONTIG
11+ select V4L2_FWNODE
12+ select IPU_BRIDGE
13+ help
14+ This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs
15+ and used for capturing images and video from camera sensors.
16+
17+ To compile this driver, say Y here! It contains 2 modules -
18+ intel_ipu6 and intel_ipu6_isys.
drivers/media/pci/intel/ipu6/Makefile+23−0
@@ -0,0 +1,23 @@
1+# SPDX-License-Identifier: GPL-2.0-only
2+
3+intel-ipu6-y := ipu6.o \
4+ ipu6-bus.o \
5+ ipu6-dma.o \
6+ ipu6-mmu.o \
7+ ipu6-buttress.o \
8+ ipu6-cpd.o \
9+ ipu6-fw-com.o
10+
11+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o
12+
13+intel-ipu6-isys-y := ipu6-isys.o \
14+ ipu6-isys-csi2.o \
15+ ipu6-fw-isys.o \
16+ ipu6-isys-video.o \
17+ ipu6-isys-queue.o \
18+ ipu6-isys-subdev.o \
19+ ipu6-isys-mcd-phy.o \
20+ ipu6-isys-jsl-phy.o \
21+ ipu6-isys-dwc-phy.o
22+
23+obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o
drivers/media/pci/intel/ipu6/ipu6-bus.c+165−0
@@ -0,0 +1,165 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013 - 2024 Intel Corporation
4+ */
5+
6+#include <linux/auxiliary_bus.h>
7+#include <linux/device.h>
8+#include <linux/dma-mapping.h>
9+#include <linux/err.h>
10+#include <linux/list.h>
11+#include <linux/mutex.h>
12+#include <linux/pci.h>
13+#include <linux/pm_domain.h>
14+#include <linux/pm_runtime.h>
15+#include <linux/slab.h>
16+
17+#include "ipu6.h"
18+#include "ipu6-bus.h"
19+#include "ipu6-buttress.h"
20+#include "ipu6-dma.h"
21+
22+static int bus_pm_runtime_suspend(struct device *dev)
23+{
24+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
25+ int ret;
26+
27+ ret = pm_generic_runtime_suspend(dev);
28+ if (ret)
29+ return ret;
30+
31+ ret = ipu6_buttress_power(dev, adev->ctrl, false);
32+ if (!ret)
33+ return 0;
34+
35+ dev_err(dev, "power down failed!\n");
36+
37+ /* Powering down failed, attempt to resume device now */
38+ ret = pm_generic_runtime_resume(dev);
39+ if (!ret)
40+ return -EBUSY;
41+
42+ return -EIO;
43+}
44+
45+static int bus_pm_runtime_resume(struct device *dev)
46+{
47+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
48+ int ret;
49+
50+ ret = ipu6_buttress_power(dev, adev->ctrl, true);
51+ if (ret)
52+ return ret;
53+
54+ ret = pm_generic_runtime_resume(dev);
55+ if (ret)
56+ goto out_err;
57+
58+ return 0;
59+
60+out_err:
61+ ipu6_buttress_power(dev, adev->ctrl, false);
62+
63+ return -EBUSY;
64+}
65+
66+static struct dev_pm_domain ipu6_bus_pm_domain = {
67+ .ops = {
68+ .runtime_suspend = bus_pm_runtime_suspend,
69+ .runtime_resume = bus_pm_runtime_resume,
70+ },
71+};
72+
73+static DEFINE_MUTEX(ipu6_bus_mutex);
74+
75+static void ipu6_bus_release(struct device *dev)
76+{
77+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
78+
79+ kfree(adev->pdata);
80+ kfree(adev);
81+}
82+
83+struct ipu6_bus_device *
84+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
85+ void *pdata, struct ipu6_buttress_ctrl *ctrl,
86+ char *name)
87+{
88+ struct auxiliary_device *auxdev;
89+ struct ipu6_bus_device *adev;
90+ struct ipu6_device *isp = pci_get_drvdata(pdev);
91+ int ret;
92+
93+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
94+ if (!adev)
95+ return ERR_PTR(-ENOMEM);
96+
97+ adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
98+ IPU6_MMU_ADDR_BITS_NON_SECURE);
99+ adev->isp = isp;
100+ adev->ctrl = ctrl;
101+ adev->pdata = pdata;
102+ auxdev = &adev->auxdev;
103+ auxdev->name = name;
104+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
105+ PCI_DEVID(pdev->bus->number, pdev->devfn);
106+
107+ auxdev->dev.parent = parent;
108+ auxdev->dev.release = ipu6_bus_release;
109+ auxdev->dev.dma_ops = &ipu6_dma_ops;
110+ auxdev->dev.dma_mask = &adev->dma_mask;
111+ auxdev->dev.dma_parms = pdev->dev.dma_parms;
112+ auxdev->dev.coherent_dma_mask = adev->dma_mask;
113+
114+ ret = auxiliary_device_init(auxdev);
115+ if (ret < 0) {
116+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
117+ ret);
118+ kfree(adev);
119+ return ERR_PTR(ret);
120+ }
121+
122+ dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
123+
124+ pm_runtime_forbid(&adev->auxdev.dev);
125+ pm_runtime_enable(&adev->auxdev.dev);
126+
127+ return adev;
128+}
129+
130+int ipu6_bus_add_device(struct ipu6_bus_device *adev)
131+{
132+ struct auxiliary_device *auxdev = &adev->auxdev;
133+ int ret;
134+
135+ ret = auxiliary_device_add(auxdev);
136+ if (ret) {
137+ auxiliary_device_uninit(auxdev);
138+ return ret;
139+ }
140+
141+ mutex_lock(&ipu6_bus_mutex);
142+ list_add(&adev->list, &adev->isp->devices);
143+ mutex_unlock(&ipu6_bus_mutex);
144+
145+ pm_runtime_allow(&auxdev->dev);
146+
147+ return 0;
148+}
149+
150+void ipu6_bus_del_devices(struct pci_dev *pdev)
151+{
152+ struct ipu6_device *isp = pci_get_drvdata(pdev);
153+ struct ipu6_bus_device *adev, *save;
154+
155+ mutex_lock(&ipu6_bus_mutex);
156+
157+ list_for_each_entry_safe(adev, save, &isp->devices, list) {
158+ pm_runtime_disable(&adev->auxdev.dev);
159+ list_del(&adev->list);
160+ auxiliary_device_delete(&adev->auxdev);
161+ auxiliary_device_uninit(&adev->auxdev);
162+ }
163+
164+ mutex_unlock(&ipu6_bus_mutex);
165+}
drivers/media/pci/intel/ipu6/ipu6-bus.h+58−0
@@ -0,0 +1,58 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013 - 2024 Intel Corporation */
3+
4+#ifndef IPU6_BUS_H
5+#define IPU6_BUS_H
6+
7+#include <linux/auxiliary_bus.h>
8+#include <linux/container_of.h>
9+#include <linux/device.h>
10+#include <linux/irqreturn.h>
11+#include <linux/list.h>
12+#include <linux/scatterlist.h>
13+#include <linux/types.h>
14+
15+struct firmware;
16+struct pci_dev;
17+
18+#define IPU6_BUS_NAME IPU6_NAME "-bus"
19+
20+struct ipu6_buttress_ctrl;
21+
22+struct ipu6_bus_device {
23+ struct auxiliary_device auxdev;
24+ struct auxiliary_driver *auxdrv;
25+ const struct ipu6_auxdrv_data *auxdrv_data;
26+ struct list_head list;
27+ void *pdata;
28+ struct ipu6_mmu *mmu;
29+ struct ipu6_device *isp;
30+ struct ipu6_buttress_ctrl *ctrl;
31+ u64 dma_mask;
32+ const struct firmware *fw;
33+ struct sg_table fw_sgt;
34+ u64 *pkg_dir;
35+ dma_addr_t pkg_dir_dma_addr;
36+ unsigned int pkg_dir_size;
37+};
38+
39+struct ipu6_auxdrv_data {
40+ irqreturn_t (*isr)(struct ipu6_bus_device *adev);
41+ irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev);
42+ bool wake_isr_thread;
43+};
44+
45+#define to_ipu6_bus_device(_dev) \
46+ container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev)
47+#define auxdev_to_adev(_auxdev) \
48+ container_of(_auxdev, struct ipu6_bus_device, auxdev)
49+#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
50+
51+struct ipu6_bus_device *
52+ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
53+ void *pdata, struct ipu6_buttress_ctrl *ctrl,
54+ char *name);
55+int ipu6_bus_add_device(struct ipu6_bus_device *adev);
56+void ipu6_bus_del_devices(struct pci_dev *pdev);
57+
58+#endif
drivers/media/pci/intel/ipu6/ipu6-buttress.c+917−0
@@ -0,0 +1,917 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bitfield.h>
7+#include <linux/bits.h>
8+#include <linux/completion.h>
9+#include <linux/delay.h>
10+#include <linux/device.h>
11+#include <linux/dma-mapping.h>
12+#include <linux/firmware.h>
13+#include <linux/interrupt.h>
14+#include <linux/iopoll.h>
15+#include <linux/math64.h>
16+#include <linux/mm.h>
17+#include <linux/mutex.h>
18+#include <linux/pci.h>
19+#include <linux/pfn.h>
20+#include <linux/pm_runtime.h>
21+#include <linux/scatterlist.h>
22+#include <linux/slab.h>
23+#include <linux/time64.h>
24+
25+#include "ipu6.h"
26+#include "ipu6-bus.h"
27+#include "ipu6-buttress.h"
28+#include "ipu6-platform-buttress-regs.h"
29+
30+#define BOOTLOADER_STATUS_OFFSET 0x15c
31+
32+#define BOOTLOADER_MAGIC_KEY 0xb00710ad
33+
34+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
35+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
36+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
37+
38+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10
39+
40+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC)
41+
42+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC)
43+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC)
44+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC)
45+
46+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC
47+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC
48+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC)
49+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC)
50+
51+#define BUTTRESS_IPC_RESET_RETRY 2000
52+#define BUTTRESS_CSE_IPC_RESET_RETRY 4
53+#define BUTTRESS_IPC_CMD_SEND_RETRY 1
54+
55+#define BUTTRESS_MAX_CONSECUTIVE_IRQS 100
56+
57+static const u32 ipu6_adev_irq_mask[2] = {
58+ BUTTRESS_ISR_IS_IRQ,
59+ BUTTRESS_ISR_PS_IRQ
60+};
61+
62+int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
63+ struct ipu6_buttress_ipc *ipc)
64+{
65+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
66+ struct ipu6_buttress *b = &isp->buttress;
67+ u32 val = 0, csr_in_clr;
68+
69+ if (!isp->secure_mode) {
70+ dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
71+ return 0;
72+ }
73+
74+ mutex_lock(&b->ipc_mutex);
75+
76+ /* Clear-by-1 CSR (all bits), corresponding internal states. */
77+ val = readl(isp->base + ipc->csr_in);
78+ writel(val, isp->base + ipc->csr_in);
79+
80+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
81+ writel(ENTRY, isp->base + ipc->csr_out);
82+ /*
83+ * Clear-by-1 all CSR bits EXCEPT following
84+ * bits:
85+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
86+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
87+ * C. Possibly custom bits, depending on
88+ * their role.
89+ */
90+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
91+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
92+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
93+
94+ do {
95+ usleep_range(400, 500);
96+ val = readl(isp->base + ipc->csr_in);
97+ switch (val) {
98+ case ENTRY | EXIT:
99+ case ENTRY | EXIT | QUERY:
100+ /*
101+ * 1) Clear-by-1 CSR bits
102+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
103+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
104+ * 2) Set peer CSR bit
105+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
106+ */
107+ writel(ENTRY | EXIT, isp->base + ipc->csr_in);
108+ writel(QUERY, isp->base + ipc->csr_out);
109+ break;
110+ case ENTRY:
111+ case ENTRY | QUERY:
112+ /*
113+ * 1) Clear-by-1 CSR bits
114+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
115+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
116+ * 2) Set peer CSR bit
117+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
118+ */
119+ writel(ENTRY | QUERY, isp->base + ipc->csr_in);
120+ writel(ENTRY, isp->base + ipc->csr_out);
121+ break;
122+ case EXIT:
123+ case EXIT | QUERY:
124+ /*
125+ * Clear-by-1 CSR bit
126+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
127+ * 1) Clear incoming doorbell.
128+ * 2) Clear-by-1 all CSR bits EXCEPT following
129+ * bits:
130+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
131+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
132+ * C. Possibly custom bits, depending on
133+ * their role.
134+ * 3) Set peer CSR bit
135+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
136+ */
137+ writel(EXIT, isp->base + ipc->csr_in);
138+ writel(0, isp->base + ipc->db0_in);
139+ writel(csr_in_clr, isp->base + ipc->csr_in);
140+ writel(EXIT, isp->base + ipc->csr_out);
141+
142+ /*
143+ * Read csr_in again to make sure if RST_PHASE2 is done.
144+ * If csr_in is QUERY, it should be handled again.
145+ */
146+ usleep_range(200, 300);
147+ val = readl(isp->base + ipc->csr_in);
148+ if (val & QUERY) {
149+ dev_dbg(&isp->pdev->dev,
150+ "RST_PHASE2 retry csr_in = %x\n", val);
151+ break;
152+ }
153+ mutex_unlock(&b->ipc_mutex);
154+ return 0;
155+ case QUERY:
156+ /*
157+ * 1) Clear-by-1 CSR bit
158+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
159+ * 2) Set peer CSR bit
160+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1
161+ */
162+ writel(QUERY, isp->base + ipc->csr_in);
163+ writel(ENTRY, isp->base + ipc->csr_out);
164+ break;
165+ default:
166+ dev_warn_ratelimited(&isp->pdev->dev,
167+ "Unexpected CSR 0x%x\n", val);
168+ break;
169+ }
170+ } while (retries--);
171+
172+ mutex_unlock(&b->ipc_mutex);
173+ dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
174+
175+ return -ETIMEDOUT;
176+}
177+
178+static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
179+ struct ipu6_buttress_ipc *ipc)
180+{
181+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
182+ isp->base + ipc->csr_out);
183+}
184+
185+static int
186+ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
187+ struct ipu6_buttress_ipc *ipc)
188+{
189+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
190+ void __iomem *addr;
191+ int ret;
192+ u32 val;
193+
194+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
195+ isp->base + ipc->csr_out);
196+
197+ addr = isp->base + ipc->csr_in;
198+ ret = readl_poll_timeout(addr, val, val & mask, 200,
199+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
200+ if (ret) {
201+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
202+ ipu6_buttress_ipc_validity_close(isp, ipc);
203+ }
204+
205+ return ret;
206+}
207+
208+static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
209+ struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
210+{
211+ if (ipc_msg)
212+ *ipc_msg = readl(isp->base + ipc->data0_in);
213+ writel(0, isp->base + ipc->db0_in);
214+}
215+
216+static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
217+ enum ipu6_buttress_ipc_domain ipc_domain,
218+ struct ipu6_ipc_buttress_bulk_msg *msgs,
219+ u32 size)
220+{
221+ unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
222+ unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
223+ struct ipu6_buttress *b = &isp->buttress;
224+ struct ipu6_buttress_ipc *ipc;
225+ u32 val;
226+ int ret;
227+ int tout;
228+
229+ ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
230+
231+ mutex_lock(&b->ipc_mutex);
232+
233+ ret = ipu6_buttress_ipc_validity_open(isp, ipc);
234+ if (ret) {
235+ dev_err(&isp->pdev->dev, "IPC validity open failed\n");
236+ goto out;
237+ }
238+
239+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
240+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
241+
242+ for (i = 0; i < size; i++) {
243+ reinit_completion(&ipc->send_complete);
244+ if (msgs[i].require_resp)
245+ reinit_completion(&ipc->recv_complete);
246+
247+ dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
248+ msgs[i].cmd);
249+ writel(msgs[i].cmd, isp->base + ipc->data0_out);
250+ val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
251+ writel(val, isp->base + ipc->db0_out);
252+
253+ tout = wait_for_completion_timeout(&ipc->send_complete,
254+ tx_timeout_jiffies);
255+ if (!tout) {
256+ dev_err(&isp->pdev->dev, "send IPC response timeout\n");
257+ if (!retry--) {
258+ ret = -ETIMEDOUT;
259+ goto out;
260+ }
261+
262+ /* Try again if CSE is not responding on first try */
263+ writel(0, isp->base + ipc->db0_out);
264+ i--;
265+ continue;
266+ }
267+
268+ retry = BUTTRESS_IPC_CMD_SEND_RETRY;
269+
270+ if (!msgs[i].require_resp)
271+ continue;
272+
273+ tout = wait_for_completion_timeout(&ipc->recv_complete,
274+ rx_timeout_jiffies);
275+ if (!tout) {
276+ dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
277+ ret = -ETIMEDOUT;
278+ goto out;
279+ }
280+
281+ if (ipc->nack_mask &&
282+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
283+ dev_err(&isp->pdev->dev,
284+ "IPC NACK for cmd 0x%x\n", msgs[i].cmd);
285+ ret = -EIO;
286+ goto out;
287+ }
288+
289+ if (ipc->recv_data != msgs[i].expected_resp) {
290+ dev_err(&isp->pdev->dev,
291+ "expected resp: 0x%x, IPC response: 0x%x ",
292+ msgs[i].expected_resp, ipc->recv_data);
293+ ret = -EIO;
294+ goto out;
295+ }
296+ }
297+
298+ dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
299+
300+out:
301+ ipu6_buttress_ipc_validity_close(isp, ipc);
302+ mutex_unlock(&b->ipc_mutex);
303+ return ret;
304+}
305+
306+static int
307+ipu6_buttress_ipc_send(struct ipu6_device *isp,
308+ enum ipu6_buttress_ipc_domain ipc_domain,
309+ u32 ipc_msg, u32 size, bool require_resp,
310+ u32 expected_resp)
311+{
312+ struct ipu6_ipc_buttress_bulk_msg msg = {
313+ .cmd = ipc_msg,
314+ .cmd_size = size,
315+ .require_resp = require_resp,
316+ .expected_resp = expected_resp,
317+ };
318+
319+ return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
320+}
321+
322+static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
323+{
324+ irqreturn_t ret = IRQ_WAKE_THREAD;
325+
326+ if (!adev || !adev->auxdrv || !adev->auxdrv_data)
327+ return IRQ_NONE;
328+
329+ if (adev->auxdrv_data->isr)
330+ ret = adev->auxdrv_data->isr(adev);
331+
332+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
333+ ret = IRQ_NONE;
334+
335+ return ret;
336+}
337+
338+irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
339+{
340+ struct ipu6_device *isp = isp_ptr;
341+ struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
342+ struct ipu6_buttress *b = &isp->buttress;
343+ u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
344+ irqreturn_t ret = IRQ_NONE;
345+ u32 disable_irqs = 0;
346+ u32 irq_status;
347+ u32 i, count = 0;
348+
349+ pm_runtime_get_noresume(&isp->pdev->dev);
350+
351+ irq_status = readl(isp->base + reg_irq_sts);
352+ if (!irq_status) {
353+ pm_runtime_put_noidle(&isp->pdev->dev);
354+ return IRQ_NONE;
355+ }
356+
357+ do {
358+ writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
359+
360+ for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
361+ irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
362+
363+ if (!(irq_status & ipu6_adev_irq_mask[i]))
364+ continue;
365+
366+ if (r == IRQ_WAKE_THREAD) {
367+ ret = IRQ_WAKE_THREAD;
368+ disable_irqs |= ipu6_adev_irq_mask[i];
369+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
370+ ret = IRQ_HANDLED;
371+ }
372+ }
373+
374+ if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
375+ ret = IRQ_HANDLED;
376+
377+ if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
378+ dev_dbg(&isp->pdev->dev,
379+ "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
380+ ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
381+ complete(&b->cse.recv_complete);
382+ }
383+
384+ if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
385+ dev_dbg(&isp->pdev->dev,
386+ "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
387+ ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
388+ complete(&b->ish.recv_complete);
389+ }
390+
391+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
392+ dev_dbg(&isp->pdev->dev,
393+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
394+ complete(&b->cse.send_complete);
395+ }
396+
397+ if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
398+ dev_dbg(&isp->pdev->dev,
399+ "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
400+ complete(&b->ish.send_complete);
401+ }
402+
403+ if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
404+ ipu6_buttress_get_secure_mode(isp))
405+ dev_err(&isp->pdev->dev,
406+ "BUTTRESS_ISR_SAI_VIOLATION\n");
407+
408+ if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
409+ BUTTRESS_ISR_PS_FATAL_MEM_ERR))
410+ dev_err(&isp->pdev->dev,
411+ "BUTTRESS_ISR_FATAL_MEM_ERR\n");
412+
413+ if (irq_status & BUTTRESS_ISR_UFI_ERROR)
414+ dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
415+
416+ if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
417+ dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
418+ ret = IRQ_NONE;
419+ break;
420+ }
421+
422+ irq_status = readl(isp->base + reg_irq_sts);
423+ } while (irq_status);
424+
425+ if (disable_irqs)
426+ writel(BUTTRESS_IRQS & ~disable_irqs,
427+ isp->base + BUTTRESS_REG_ISR_ENABLE);
428+
429+ pm_runtime_put(&isp->pdev->dev);
430+
431+ return ret;
432+}
433+
434+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
435+{
436+ struct ipu6_device *isp = isp_ptr;
437+ struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
438+ const struct ipu6_auxdrv_data *drv_data = NULL;
439+ irqreturn_t ret = IRQ_NONE;
440+ unsigned int i;
441+
442+ for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
443+ drv_data = adev[i]->auxdrv_data;
444+ if (!drv_data)
445+ continue;
446+
447+ if (drv_data->wake_isr_thread &&
448+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
449+ ret = IRQ_HANDLED;
450+ }
451+
452+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
453+
454+ return ret;
455+}
456+
457+int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
458+ bool on)
459+{
460+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
461+ u32 pwr_sts, val;
462+ int ret;
463+
464+ if (!ctrl)
465+ return 0;
466+
467+ mutex_lock(&isp->buttress.power_mutex);
468+
469+ if (!on) {
470+ val = 0;
471+ pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
472+ } else {
473+ val = BUTTRESS_FREQ_CTL_START |
474+ FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
475+ ctrl->ratio) |
476+ FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
477+ ctrl->qos_floor) |
478+ BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
479+
480+ pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
481+ }
482+
483+ writel(val, isp->base + ctrl->freq_ctl);
484+
485+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
486+ val, (val & ctrl->pwr_sts_mask) == pwr_sts,
487+ 100, BUTTRESS_POWER_TIMEOUT_US);
488+ if (ret)
489+ dev_err(&isp->pdev->dev,
490+ "Change power status timeout with 0x%x\n", val);
491+
492+ ctrl->started = !ret && on;
493+
494+ mutex_unlock(&isp->buttress.power_mutex);
495+
496+ return ret;
497+}
498+
499+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
500+{
501+ u32 val;
502+
503+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
504+
505+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
506+}
507+
508+bool ipu6_buttress_auth_done(struct ipu6_device *isp)
509+{
510+ u32 val;
511+
512+ if (!isp->secure_mode)
513+ return true;
514+
515+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
516+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
517+
518+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
519+}
520+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
521+
522+int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
523+{
524+ int ret;
525+ u32 val;
526+
527+ if (!isp->secure_mode) {
528+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
529+ return 0;
530+ }
531+
532+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
533+ BUTTRESS_REG_FW_RESET_CTL);
534+
535+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
536+ val & BUTTRESS_FW_RESET_CTL_DONE, 500,
537+ BUTTRESS_CSE_FWRESET_TIMEOUT_US);
538+ if (ret) {
539+ dev_err(&isp->pdev->dev,
540+ "Time out while resetting authentication state\n");
541+ return ret;
542+ }
543+
544+ dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
545+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
546+ /* leave some time for HW restore */
547+ usleep_range(800, 1000);
548+
549+ return 0;
550+}
551+
552+int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
553+ const struct firmware *fw, struct sg_table *sgt)
554+{
555+ bool is_vmalloc = is_vmalloc_addr(fw->data);
556+ struct page **pages;
557+ const void *addr;
558+ unsigned long n_pages;
559+ unsigned int i;
560+ int ret;
561+
562+ if (!is_vmalloc && !virt_addr_valid(fw->data))
563+ return -EDOM;
564+
565+ n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
566+
567+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
568+ if (!pages)
569+ return -ENOMEM;
570+
571+ addr = fw->data;
572+ for (i = 0; i < n_pages; i++) {
573+ struct page *p = is_vmalloc ?
574+ vmalloc_to_page(addr) : virt_to_page(addr);
575+
576+ if (!p) {
577+ ret = -ENOMEM;
578+ goto out;
579+ }
580+ pages[i] = p;
581+ addr += PAGE_SIZE;
582+ }
583+
584+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
585+ GFP_KERNEL);
586+ if (ret) {
587+ ret = -ENOMEM;
588+ goto out;
589+ }
590+
591+ ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
592+ if (ret < 0) {
593+ ret = -ENOMEM;
594+ sg_free_table(sgt);
595+ goto out;
596+ }
597+
598+ dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
599+
600+out:
601+ kfree(pages);
602+
603+ return ret;
604+}
605+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
606+
607+void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
608+ struct sg_table *sgt)
609+{
610+ dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
611+ sg_free_table(sgt);
612+}
613+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
614+
615+int ipu6_buttress_authenticate(struct ipu6_device *isp)
616+{
617+ struct ipu6_buttress *b = &isp->buttress;
618+ struct ipu6_psys_pdata *psys_pdata;
619+ u32 data, mask, done, fail;
620+ int ret;
621+
622+ if (!isp->secure_mode) {
623+ dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
624+ return 0;
625+ }
626+
627+ psys_pdata = isp->psys->pdata;
628+
629+ mutex_lock(&b->auth_mutex);
630+
631+ if (ipu6_buttress_auth_done(isp)) {
632+ ret = 0;
633+ goto out_unlock;
634+ }
635+
636+ /*
637+ * Write address of FIT table to FW_SOURCE register
638+ * Let's use fw address. I.e. not using FIT table yet
639+ */
640+ data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
641+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
642+
643+ data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
644+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
645+
646+ /*
647+ * Write boot_load into IU2CSEDATA0
648+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
649+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
650+ */
651+ dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
652+
653+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
654+ BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
655+ 1, true,
656+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
657+ if (ret) {
658+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
659+ goto out_unlock;
660+ }
661+
662+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
663+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
664+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
665+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
666+ ((data & mask) == done ||
667+ (data & mask) == fail), 500,
668+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
669+ if (ret) {
670+ dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
671+ goto out_unlock;
672+ }
673+
674+ if ((data & mask) == fail) {
675+ dev_err(&isp->pdev->dev, "CSE auth failed\n");
676+ ret = -EINVAL;
677+ goto out_unlock;
678+ }
679+
680+ ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
681+ data, data == BOOTLOADER_MAGIC_KEY, 500,
682+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
683+ if (ret) {
684+ dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
685+ data);
686+ goto out_unlock;
687+ }
688+
689+ /*
690+ * Write authenticate_run into IU2CSEDATA0
691+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
692+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
693+ */
694+ dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
695+ ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
696+ BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
697+ 1, true,
698+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
699+ if (ret) {
700+ dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
701+ goto out_unlock;
702+ }
703+
704+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
705+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
706+ ((data & mask) == done ||
707+ (data & mask) == fail), 500,
708+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
709+ if (ret) {
710+ dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
711+ goto out_unlock;
712+ }
713+
714+ if ((data & mask) == fail) {
715+ dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
716+ ret = -EINVAL;
717+ goto out_unlock;
718+ }
719+
720+ dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
721+
722+out_unlock:
723+ mutex_unlock(&b->auth_mutex);
724+
725+ return ret;
726+}
727+
728+static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
729+{
730+ u32 val, mask, done;
731+ int ret;
732+
733+ mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
734+
735+ writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
736+ isp->base + BUTTRESS_REG_FABRIC_CMD);
737+
738+ val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
739+ val = FIELD_GET(mask, val);
740+ if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
741+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
742+ return -EINVAL;
743+ }
744+
745+ done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
746+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
747+ FIELD_GET(mask, val) == done, 500,
748+ BUTTRESS_TSC_SYNC_TIMEOUT_US);
749+ if (ret)
750+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
751+
752+ return ret;
753+}
754+
755+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
756+{
757+ unsigned int i;
758+
759+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
760+ u32 val;
761+ int ret;
762+
763+ ret = ipu6_buttress_send_tsc_request(isp);
764+ if (ret != -ETIMEDOUT)
765+ return ret;
766+
767+ val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
768+ val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
769+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
770+ val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
771+ writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
772+ }
773+
774+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
775+
776+ return -ETIMEDOUT;
777+}
778+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
779+
780+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
781+{
782+ u32 tsc_hi_1, tsc_hi_2, tsc_lo;
783+ unsigned long flags;
784+
785+ local_irq_save(flags);
786+ tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
787+ tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
788+ tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
789+ if (tsc_hi_1 == tsc_hi_2) {
790+ *val = (u64)tsc_hi_1 << 32 | tsc_lo;
791+ } else {
792+ /* Check if TSC has rolled over */
793+ if (tsc_lo & BIT(31))
794+ *val = (u64)tsc_hi_1 << 32 | tsc_lo;
795+ else
796+ *val = (u64)tsc_hi_2 << 32 | tsc_lo;
797+ }
798+ local_irq_restore(flags);
799+}
800+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
801+
802+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
803+{
804+ u64 ns = ticks * 10000;
805+
806+ /*
807+ * converting TSC tick count to ns is calculated by:
808+ * Example (TSC clock frequency is 19.2MHz):
809+ * ns = ticks * 1000 000 000 / 19.2Mhz
810+ * = ticks * 1000 000 000 / 19200000Hz
811+ * = ticks * 10000 / 192 ns
812+ */
813+ return div_u64(ns, isp->buttress.ref_clk);
814+}
815+EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
816+
817+void ipu6_buttress_restore(struct ipu6_device *isp)
818+{
819+ struct ipu6_buttress *b = &isp->buttress;
820+
821+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
822+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
823+ writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
824+}
825+
826+int ipu6_buttress_init(struct ipu6_device *isp)
827+{
828+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
829+ struct ipu6_buttress *b = &isp->buttress;
830+ u32 val;
831+
832+ mutex_init(&b->power_mutex);
833+ mutex_init(&b->auth_mutex);
834+ mutex_init(&b->cons_mutex);
835+ mutex_init(&b->ipc_mutex);
836+ init_completion(&b->ish.send_complete);
837+ init_completion(&b->cse.send_complete);
838+ init_completion(&b->ish.recv_complete);
839+ init_completion(&b->cse.recv_complete);
840+
841+ b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
842+ b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
843+ b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
844+ b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
845+ b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
846+ b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
847+ b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
848+ b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
849+
850+ /* no ISH on IPU6 */
851+ memset(&b->ish, 0, sizeof(b->ish));
852+ INIT_LIST_HEAD(&b->constraints);
853+
854+ isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
855+ dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
856+ isp->secure_mode ? "secure" : "non-secure",
857+ readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
858+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
859+
860+ b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
861+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
862+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
863+
864+ /* get ref_clk frequency by reading the indication in btrs control */
865+ val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
866+ val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
867+
868+ switch (val) {
869+ case 0x0:
870+ b->ref_clk = 240;
871+ break;
872+ case 0x1:
873+ b->ref_clk = 192;
874+ break;
875+ case 0x2:
876+ b->ref_clk = 384;
877+ break;
878+ default:
879+ dev_warn(&isp->pdev->dev,
880+ "Unsupported ref clock, use 19.2Mhz by default.\n");
881+ b->ref_clk = 192;
882+ break;
883+ }
884+
885+ /* Retry couple of times in case of CSE initialization is delayed */
886+ do {
887+ ret = ipu6_buttress_ipc_reset(isp, &b->cse);
888+ if (ret) {
889+ dev_warn(&isp->pdev->dev,
890+ "IPC reset protocol failed, retrying\n");
891+ } else {
892+ dev_dbg(&isp->pdev->dev, "IPC reset done\n");
893+ return 0;
894+ }
895+ } while (ipc_reset_retry--);
896+
897+ dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
898+
899+ mutex_destroy(&b->power_mutex);
900+ mutex_destroy(&b->auth_mutex);
901+ mutex_destroy(&b->cons_mutex);
902+ mutex_destroy(&b->ipc_mutex);
903+
904+ return ret;
905+}
906+
907+void ipu6_buttress_exit(struct ipu6_device *isp)
908+{
909+ struct ipu6_buttress *b = &isp->buttress;
910+
911+ writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
912+
913+ mutex_destroy(&b->power_mutex);
914+ mutex_destroy(&b->auth_mutex);
915+ mutex_destroy(&b->cons_mutex);
916+ mutex_destroy(&b->ipc_mutex);
917+}
drivers/media/pci/intel/ipu6/ipu6-buttress.h+92−0
@@ -0,0 +1,92 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_BUTTRESS_H
5+#define IPU6_BUTTRESS_H
6+
7+#include <linux/completion.h>
8+#include <linux/irqreturn.h>
9+#include <linux/list.h>
10+#include <linux/mutex.h>
11+
12+struct device;
13+struct firmware;
14+struct ipu6_device;
15+struct ipu6_bus_device;
16+
17+#define BUTTRESS_PS_FREQ_STEP 25U
18+#define BUTTRESS_MIN_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 8)
19+#define BUTTRESS_MAX_FORCE_PS_FREQ (BUTTRESS_PS_FREQ_STEP * 32)
20+
21+#define BUTTRESS_IS_FREQ_STEP 25U
22+#define BUTTRESS_MIN_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 8)
23+#define BUTTRESS_MAX_FORCE_IS_FREQ (BUTTRESS_IS_FREQ_STEP * 22)
24+
25+struct ipu6_buttress_ctrl {
26+ u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
27+ unsigned int ratio;
28+ unsigned int qos_floor;
29+ bool started;
30+};
31+
32+struct ipu6_buttress_ipc {
33+ struct completion send_complete;
34+ struct completion recv_complete;
35+ u32 nack;
36+ u32 nack_mask;
37+ u32 recv_data;
38+ u32 csr_out;
39+ u32 csr_in;
40+ u32 db0_in;
41+ u32 db0_out;
42+ u32 data0_out;
43+ u32 data0_in;
44+};
45+
46+struct ipu6_buttress {
47+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
48+ struct ipu6_buttress_ipc cse;
49+ struct ipu6_buttress_ipc ish;
50+ struct list_head constraints;
51+ u32 wdt_cached_value;
52+ bool force_suspend;
53+ u32 ref_clk;
54+};
55+
56+enum ipu6_buttress_ipc_domain {
57+ IPU6_BUTTRESS_IPC_CSE,
58+ IPU6_BUTTRESS_IPC_ISH,
59+};
60+
61+struct ipu6_ipc_buttress_bulk_msg {
62+ u32 cmd;
63+ u32 expected_resp;
64+ bool require_resp;
65+ u8 cmd_size;
66+};
67+
68+int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
69+ struct ipu6_buttress_ipc *ipc);
70+int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
71+ const struct firmware *fw,
72+ struct sg_table *sgt);
73+void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
74+ struct sg_table *sgt);
75+int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
76+ bool on);
77+bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
78+int ipu6_buttress_authenticate(struct ipu6_device *isp);
79+int ipu6_buttress_reset_authentication(struct ipu6_device *isp);
80+bool ipu6_buttress_auth_done(struct ipu6_device *isp);
81+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp);
82+void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val);
83+u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp);
84+
85+irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr);
86+irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr);
87+int ipu6_buttress_init(struct ipu6_device *isp);
88+void ipu6_buttress_exit(struct ipu6_device *isp);
89+void ipu6_buttress_csi_port_config(struct ipu6_device *isp,
90+ u32 legacy, u32 combo);
91+void ipu6_buttress_restore(struct ipu6_device *isp);
92+#endif /* IPU6_BUTTRESS_H */
drivers/media/pci/intel/ipu6/ipu6-cpd.c+362−0
@@ -0,0 +1,362 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bitfield.h>
7+#include <linux/bits.h>
8+#include <linux/err.h>
9+#include <linux/dma-mapping.h>
10+#include <linux/gfp_types.h>
11+#include <linux/math64.h>
12+#include <linux/sizes.h>
13+#include <linux/types.h>
14+
15+#include "ipu6.h"
16+#include "ipu6-bus.h"
17+#include "ipu6-cpd.h"
18+
19+/* 15 entries + header*/
20+#define MAX_PKG_DIR_ENT_CNT 16
21+/* 2 qword per entry/header */
22+#define PKG_DIR_ENT_LEN 2
23+/* PKG_DIR size in bytes */
24+#define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \
25+ (PKG_DIR_ENT_LEN) * sizeof(u64))
26+/* _IUPKDR_ */
27+#define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL
28+
29+/* $CPD */
30+#define CPD_HDR_MARK 0x44504324
31+
32+#define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32))
33+#define MAX_METADATA_SIZE SZ_64K
34+
35+#define MAX_COMPONENT_ID 127
36+#define MAX_COMPONENT_VERSION 0xffff
37+
38+#define MANIFEST_IDX 0
39+#define METADATA_IDX 1
40+#define MODULEDATA_IDX 2
41+/*
42+ * PKG_DIR Entry (type == id)
43+ * 63:56 55 54:48 47:32 31:24 23:0
44+ * Rsvd Rsvd Type Version Rsvd Size
45+ */
46+#define PKG_DIR_SIZE_MASK GENMASK(23, 0)
47+#define PKG_DIR_VERSION_MASK GENMASK(47, 32)
48+#define PKG_DIR_TYPE_MASK GENMASK(54, 48)
49+
50+static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
51+ u8 idx)
52+{
53+ const struct ipu6_cpd_hdr *cpd_hdr = cpd;
54+ const struct ipu6_cpd_ent *ent;
55+
56+ ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
57+ return ent + idx;
58+}
59+
60+#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
61+#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
62+#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
63+
64+static const struct ipu6_cpd_metadata_cmpnt_hdr *
65+ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
66+ unsigned int metadata_size, u8 idx)
67+{
68+ size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
69+ size_t cmpnt_count = metadata_size - extn_size;
70+
71+ cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
72+
73+ if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
74+ dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
75+ idx);
76+ return ERR_PTR(-EINVAL);
77+ }
78+
79+ return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
80+}
81+
82+static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
83+ const void *metadata,
84+ unsigned int metadata_size, u8 idx)
85+{
86+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
87+
88+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
89+ if (IS_ERR(cmpnt))
90+ return PTR_ERR(cmpnt);
91+
92+ return cmpnt->ver;
93+}
94+
95+static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
96+ const void *metadata,
97+ unsigned int metadata_size, u8 idx)
98+{
99+ const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
100+
101+ cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
102+ if (IS_ERR(cmpnt))
103+ return PTR_ERR(cmpnt);
104+
105+ return cmpnt->id;
106+}
107+
108+static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
109+ const void *module_data,
110+ unsigned int module_data_size,
111+ dma_addr_t dma_addr_module_data,
112+ u64 *pkg_dir, const void *metadata,
113+ unsigned int metadata_size)
114+{
115+ const struct ipu6_cpd_module_data_hdr *module_data_hdr;
116+ const struct ipu6_cpd_hdr *dir_hdr;
117+ const struct ipu6_cpd_ent *dir_ent;
118+ unsigned int i;
119+ u8 len;
120+
121+ if (!module_data)
122+ return -EINVAL;
123+
124+ module_data_hdr = module_data;
125+ dir_hdr = module_data + module_data_hdr->hdr_len;
126+ len = dir_hdr->hdr_len;
127+ dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
128+
129+ pkg_dir[0] = PKG_DIR_HDR_MARK;
130+ /* pkg_dir entry count = component count + pkg_dir header */
131+ pkg_dir[1] = dir_hdr->ent_cnt + 1;
132+
133+ for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
134+ u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)];
135+ int ver, id;
136+
137+ *p++ = dma_addr_module_data + dir_ent->offset;
138+ id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
139+ metadata_size, i);
140+ if (id < 0 || id > MAX_COMPONENT_ID) {
141+ dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
142+ return -EINVAL;
143+ }
144+
145+ ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
146+ metadata_size, i);
147+ if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
148+ dev_err(&isp->pdev->dev,
149+ "Invalid CPD component version\n");
150+ return -EINVAL;
151+ }
152+
153+ *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
154+ FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
155+ FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
156+ }
157+
158+ return 0;
159+}
160+
161+int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
162+{
163+ dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
164+ const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
165+ struct device *dev = &adev->auxdev.dev;
166+ struct ipu6_device *isp = adev->isp;
167+ unsigned int man_sz, met_sz;
168+ void *pkg_dir_pos;
169+ int ret;
170+
171+ man_ent = ipu6_cpd_get_manifest(src);
172+ man_sz = man_ent->len;
173+
174+ met_ent = ipu6_cpd_get_metadata(src);
175+ met_sz = met_ent->len;
176+
177+ adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
178+ adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,
179+ &adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
180+ if (!adev->pkg_dir)
181+ return -ENOMEM;
182+
183+ /*
184+ * pkg_dir entry/header:
185+ * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0
186+ * N Address/Offset/"_IUPKDR_"
187+ * N + 1 | rsvd | rsvd | type | ver | rsvd | size
188+ *
189+ * We can ignore other fields that size in N + 1 qword as they
190+ * are 0 anyway. Just setting size for now.
191+ */
192+
193+ ent = ipu6_cpd_get_moduledata(src);
194+
195+ ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
196+ ent->len, dma_addr_src + ent->offset,
197+ adev->pkg_dir, src + met_ent->offset,
198+ met_ent->len);
199+ if (ret) {
200+ dev_err(&isp->pdev->dev, "Failed to parse module data\n");
201+ dma_free_attrs(dev, adev->pkg_dir_size,
202+ adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
203+ return ret;
204+ }
205+
206+ /* Copy manifest after pkg_dir */
207+ pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
208+ memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
209+
210+ /* Copy metadata after manifest */
211+ pkg_dir_pos += man_sz;
212+ memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
213+
214+ dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,
215+ 0, adev->pkg_dir_size, DMA_TO_DEVICE);
216+
217+ return 0;
218+}
219+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
220+
221+void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
222+{
223+ dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,
224+ adev->pkg_dir_dma_addr, 0);
225+}
226+EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
227+
228+static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
229+ unsigned long cpd_size,
230+ unsigned long data_size)
231+{
232+ const struct ipu6_cpd_hdr *cpd_hdr = cpd;
233+ const struct ipu6_cpd_ent *ent;
234+ unsigned int i;
235+ u8 len;
236+
237+ len = cpd_hdr->hdr_len;
238+
239+ /* Ensure cpd hdr is within moduledata */
240+ if (cpd_size < len) {
241+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
242+ return -EINVAL;
243+ }
244+
245+ /* Sanity check for CPD header */
246+ if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
247+ dev_err(&isp->pdev->dev, "Invalid CPD header\n");
248+ return -EINVAL;
249+ }
250+
251+ /* Ensure that all entries are within moduledata */
252+ ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
253+ for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
254+ if (data_size < ent->offset ||
255+ data_size - ent->offset < ent->len) {
256+ dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
257+ return -EINVAL;
258+ }
259+ }
260+
261+ return 0;
262+}
263+
264+static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
265+ const void *moduledata,
266+ u32 moduledata_size)
267+{
268+ const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
269+ int ret;
270+
271+ /* Ensure moduledata hdr is within moduledata */
272+ if (moduledata_size < sizeof(*mod_hdr) ||
273+ moduledata_size < mod_hdr->hdr_len) {
274+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
275+ return -EINVAL;
276+ }
277+
278+ dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
279+ ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
280+ moduledata_size - mod_hdr->hdr_len,
281+ moduledata_size);
282+ if (ret) {
283+ dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
284+ return ret;
285+ }
286+
287+ return 0;
288+}
289+
290+static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
291+ const void *metadata, u32 meta_size)
292+{
293+ const struct ipu6_cpd_metadata_extn *extn = metadata;
294+
295+ /* Sanity check for metadata size */
296+ if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
297+ dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
298+ return -EINVAL;
299+ }
300+
301+ /* Validate extension and image types */
302+ if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
303+ extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
304+ dev_err(&isp->pdev->dev,
305+ "Invalid CPD metadata descriptor img_type (%d)\n",
306+ extn->img_type);
307+ return -EINVAL;
308+ }
309+
310+ /* Validate metadata size multiple of metadata components */
311+ if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
312+ dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
313+ return -EINVAL;
314+ }
315+
316+ return 0;
317+}
318+
319+int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
320+ unsigned long cpd_file_size)
321+{
322+ const struct ipu6_cpd_hdr *hdr = cpd_file;
323+ const struct ipu6_cpd_ent *ent;
324+ int ret;
325+
326+ ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
327+ cpd_file_size);
328+ if (ret) {
329+ dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
330+ return ret;
331+ }
332+
333+ /* Check for CPD file marker */
334+ if (hdr->hdr_mark != CPD_HDR_MARK) {
335+ dev_err(&isp->pdev->dev, "Invalid CPD header\n");
336+ return -EINVAL;
337+ }
338+
339+ /* Sanity check for manifest size */
340+ ent = ipu6_cpd_get_manifest(cpd_file);
341+ if (ent->len > MAX_MANIFEST_SIZE) {
342+ dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
343+ return -EINVAL;
344+ }
345+
346+ /* Validate metadata */
347+ ent = ipu6_cpd_get_metadata(cpd_file);
348+ ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
349+ if (ret) {
350+ dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
351+ return ret;
352+ }
353+
354+ /* Validate moduledata */
355+ ent = ipu6_cpd_get_moduledata(cpd_file);
356+ ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
357+ ent->len);
358+ if (ret)
359+ dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
360+
361+ return ret;
362+}
drivers/media/pci/intel/ipu6/ipu6-cpd.h+105−0
@@ -0,0 +1,105 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2015--2024 Intel Corporation */
3+
4+#ifndef IPU6_CPD_H
5+#define IPU6_CPD_H
6+
7+struct ipu6_device;
8+struct ipu6_bus_device;
9+
10+#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION 7
11+#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION 11
12+#define IPU6_CPD_SIZE_OF_COMPONENT_NAME 12
13+
14+#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT 0x10
15+
16+#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED 0
17+#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER 1
18+#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE 2
19+
20+#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX 0
21+#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX 1
22+
23+#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE 3
24+
25+#define IPU6_CPD_METADATA_HASH_KEY_SIZE 48
26+#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE 32
27+
28+struct ipu6_cpd_module_data_hdr {
29+ u32 hdr_len;
30+ u32 endian;
31+ u32 fw_pkg_date;
32+ u32 hive_sdk_date;
33+ u32 compiler_date;
34+ u32 target_platform_type;
35+ u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION];
36+ u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION];
37+ u8 rsvd[2];
38+} __packed;
39+
40+/*
41+ * ipu6_cpd_hdr structure updated as the chksum and
42+ * sub_partition_name is unused on host side
43+ * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10)
44+ * CSE layout version 1.7 for IPU6 (hdr_len = 0x14)
45+ */
46+struct ipu6_cpd_hdr {
47+ u32 hdr_mark;
48+ u32 ent_cnt;
49+ u8 hdr_ver;
50+ u8 ent_ver;
51+ u8 hdr_len;
52+} __packed;
53+
54+struct ipu6_cpd_ent {
55+ u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME];
56+ u32 offset;
57+ u32 len;
58+ u8 rsvd[4];
59+} __packed;
60+
61+struct ipu6_cpd_metadata_cmpnt_hdr {
62+ u32 id;
63+ u32 size;
64+ u32 ver;
65+} __packed;
66+
67+struct ipu6_cpd_metadata_cmpnt {
68+ struct ipu6_cpd_metadata_cmpnt_hdr hdr;
69+ u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE];
70+ u32 entry_point;
71+ u32 icache_base_offs;
72+ u8 attrs[16];
73+} __packed;
74+
75+struct ipu6se_cpd_metadata_cmpnt {
76+ struct ipu6_cpd_metadata_cmpnt_hdr hdr;
77+ u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE];
78+ u32 entry_point;
79+ u32 icache_base_offs;
80+ u8 attrs[16];
81+} __packed;
82+
83+struct ipu6_cpd_metadata_extn {
84+ u32 extn_type;
85+ u32 len;
86+ u32 img_type;
87+ u8 rsvd[16];
88+} __packed;
89+
90+struct ipu6_cpd_client_pkg_hdr {
91+ u32 prog_list_offs;
92+ u32 prog_list_size;
93+ u32 prog_desc_offs;
94+ u32 prog_desc_size;
95+ u32 pg_manifest_offs;
96+ u32 pg_manifest_size;
97+ u32 prog_bin_offs;
98+ u32 prog_bin_size;
99+} __packed;
100+
101+int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src);
102+void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev);
103+int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
104+ unsigned long cpd_file_size);
105+#endif /* IPU6_CPD_H */
drivers/media/pci/intel/ipu6/ipu6-dma.c+502−0
@@ -0,0 +1,502 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/cacheflush.h>
7+#include <linux/dma-mapping.h>
8+#include <linux/iova.h>
9+#include <linux/list.h>
10+#include <linux/mm.h>
11+#include <linux/vmalloc.h>
12+#include <linux/scatterlist.h>
13+#include <linux/slab.h>
14+#include <linux/types.h>
15+
16+#include "ipu6.h"
17+#include "ipu6-bus.h"
18+#include "ipu6-dma.h"
19+#include "ipu6-mmu.h"
20+
21+struct vm_info {
22+ struct list_head list;
23+ struct page **pages;
24+ dma_addr_t ipu6_iova;
25+ void *vaddr;
26+ unsigned long size;
27+};
28+
29+static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova)
30+{
31+ struct vm_info *info, *save;
32+
33+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
34+ if (iova >= info->ipu6_iova &&
35+ iova < (info->ipu6_iova + info->size))
36+ return info;
37+ }
38+
39+ return NULL;
40+}
41+
42+static void __dma_clear_buffer(struct page *page, size_t size,
43+ unsigned long attrs)
44+{
45+ void *ptr;
46+
47+ if (!page)
48+ return;
49+ /*
50+ * Ensure that the allocated pages are zeroed, and that any data
51+ * lurking in the kernel direct-mapped region is invalidated.
52+ */
53+ ptr = page_address(page);
54+ memset(ptr, 0, size);
55+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
56+ clflush_cache_range(ptr, size);
57+}
58+
59+static struct page **__dma_alloc_buffer(struct device *dev, size_t size,
60+ gfp_t gfp, unsigned long attrs)
61+{
62+ int count = PHYS_PFN(size);
63+ int array_size = count * sizeof(struct page *);
64+ struct page **pages;
65+ int i = 0;
66+
67+ pages = kvzalloc(array_size, GFP_KERNEL);
68+ if (!pages)
69+ return NULL;
70+
71+ gfp |= __GFP_NOWARN;
72+
73+ while (count) {
74+ int j, order = __fls(count);
75+
76+ pages[i] = alloc_pages(gfp, order);
77+ while (!pages[i] && order)
78+ pages[i] = alloc_pages(gfp, --order);
79+ if (!pages[i])
80+ goto error;
81+
82+ if (order) {
83+ split_page(pages[i], order);
84+ j = 1 << order;
85+ while (j--)
86+ pages[i + j] = pages[i] + j;
87+ }
88+
89+ __dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
90+ i += 1 << order;
91+ count -= 1 << order;
92+ }
93+
94+ return pages;
95+error:
96+ while (i--)
97+ if (pages[i])
98+ __free_pages(pages[i], 0);
99+ kvfree(pages);
100+ return NULL;
101+}
102+
103+static void __dma_free_buffer(struct device *dev, struct page **pages,
104+ size_t size, unsigned long attrs)
105+{
106+ int count = PHYS_PFN(size);
107+ unsigned int i;
108+
109+ for (i = 0; i < count && pages[i]; i++) {
110+ __dma_clear_buffer(pages[i], PAGE_SIZE, attrs);
111+ __free_pages(pages[i], 0);
112+ }
113+
114+ kvfree(pages);
115+}
116+
117+static void ipu6_dma_sync_single_for_cpu(struct device *dev,
118+ dma_addr_t dma_handle,
119+ size_t size,
120+ enum dma_data_direction dir)
121+{
122+ void *vaddr;
123+ u32 offset;
124+ struct vm_info *info;
125+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
126+
127+ info = get_vm_info(mmu, dma_handle);
128+ if (WARN_ON(!info))
129+ return;
130+
131+ offset = dma_handle - info->ipu6_iova;
132+ if (WARN_ON(size > (info->size - offset)))
133+ return;
134+
135+ vaddr = info->vaddr + offset;
136+ clflush_cache_range(vaddr, size);
137+}
138+
139+static void ipu6_dma_sync_sg_for_cpu(struct device *dev,
140+ struct scatterlist *sglist,
141+ int nents, enum dma_data_direction dir)
142+{
143+ struct scatterlist *sg;
144+ int i;
145+
146+ for_each_sg(sglist, sg, nents, i)
147+ clflush_cache_range(page_to_virt(sg_page(sg)), sg->length);
148+}
149+
150+static void *ipu6_dma_alloc(struct device *dev, size_t size,
151+ dma_addr_t *dma_handle, gfp_t gfp,
152+ unsigned long attrs)
153+{
154+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
155+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
156+ dma_addr_t pci_dma_addr, ipu6_iova;
157+ struct vm_info *info;
158+ unsigned long count;
159+ struct page **pages;
160+ struct iova *iova;
161+ unsigned int i;
162+ int ret;
163+
164+ info = kzalloc(sizeof(*info), GFP_KERNEL);
165+ if (!info)
166+ return NULL;
167+
168+ size = PAGE_ALIGN(size);
169+ count = PHYS_PFN(size);
170+
171+ iova = alloc_iova(&mmu->dmap->iovad, count,
172+ PHYS_PFN(dma_get_mask(dev)), 0);
173+ if (!iova)
174+ goto out_kfree;
175+
176+ pages = __dma_alloc_buffer(dev, size, gfp, attrs);
177+ if (!pages)
178+ goto out_free_iova;
179+
180+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
181+ size, iova->pfn_lo, iova->pfn_hi);
182+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
183+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
184+ PAGE_SIZE, DMA_BIDIRECTIONAL,
185+ attrs);
186+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
187+ &pci_dma_addr);
188+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
189+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
190+ goto out_unmap;
191+ }
192+
193+ ret = ipu6_mmu_map(mmu->dmap->mmu_info,
194+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
195+ PAGE_SIZE);
196+ if (ret) {
197+ dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed",
198+ i, &pci_dma_addr);
199+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
200+ PAGE_SIZE, DMA_BIDIRECTIONAL,
201+ attrs);
202+ goto out_unmap;
203+ }
204+ }
205+
206+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
207+ if (!info->vaddr)
208+ goto out_unmap;
209+
210+ *dma_handle = PFN_PHYS(iova->pfn_lo);
211+
212+ info->pages = pages;
213+ info->ipu6_iova = *dma_handle;
214+ info->size = size;
215+ list_add(&info->list, &mmu->vma_list);
216+
217+ return info->vaddr;
218+
219+out_unmap:
220+ while (i--) {
221+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
222+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
223+ ipu6_iova);
224+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
225+ DMA_BIDIRECTIONAL, attrs);
226+
227+ ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE);
228+ }
229+
230+ __dma_free_buffer(dev, pages, size, attrs);
231+
232+out_free_iova:
233+ __free_iova(&mmu->dmap->iovad, iova);
234+out_kfree:
235+ kfree(info);
236+
237+ return NULL;
238+}
239+
240+static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr,
241+ dma_addr_t dma_handle,
242+ unsigned long attrs)
243+{
244+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
245+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
246+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
247+ dma_addr_t pci_dma_addr, ipu6_iova;
248+ struct vm_info *info;
249+ struct page **pages;
250+ unsigned int i;
251+
252+ if (WARN_ON(!iova))
253+ return;
254+
255+ info = get_vm_info(mmu, dma_handle);
256+ if (WARN_ON(!info))
257+ return;
258+
259+ if (WARN_ON(!info->vaddr))
260+ return;
261+
262+ if (WARN_ON(!info->pages))
263+ return;
264+
265+ list_del(&info->list);
266+
267+ size = PAGE_ALIGN(size);
268+
269+ pages = info->pages;
270+
271+ vunmap(vaddr);
272+
273+ for (i = 0; i < PHYS_PFN(size); i++) {
274+ ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
275+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
276+ ipu6_iova);
277+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
278+ DMA_BIDIRECTIONAL, attrs);
279+ }
280+
281+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
282+ PFN_PHYS(iova_size(iova)));
283+
284+ __dma_free_buffer(dev, pages, size, attrs);
285+
286+ mmu->tlb_invalidate(mmu);
287+
288+ __free_iova(&mmu->dmap->iovad, iova);
289+
290+ kfree(info);
291+}
292+
293+static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma,
294+ void *addr, dma_addr_t iova, size_t size,
295+ unsigned long attrs)
296+{
297+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
298+ size_t count = PHYS_PFN(PAGE_ALIGN(size));
299+ struct vm_info *info;
300+ size_t i;
301+ int ret;
302+
303+ info = get_vm_info(mmu, iova);
304+ if (!info)
305+ return -EFAULT;
306+
307+ if (!info->vaddr)
308+ return -EFAULT;
309+
310+ if (vma->vm_start & ~PAGE_MASK)
311+ return -EINVAL;
312+
313+ if (size > info->size)
314+ return -EFAULT;
315+
316+ for (i = 0; i < count; i++) {
317+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
318+ info->pages[i]);
319+ if (ret < 0)
320+ return ret;
321+ }
322+
323+ return 0;
324+}
325+
326+static void ipu6_dma_unmap_sg(struct device *dev,
327+ struct scatterlist *sglist,
328+ int nents, enum dma_data_direction dir,
329+ unsigned long attrs)
330+{
331+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
332+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
333+ struct iova *iova = find_iova(&mmu->dmap->iovad,
334+ PHYS_PFN(sg_dma_address(sglist)));
335+ int i, npages, count;
336+ struct scatterlist *sg;
337+ dma_addr_t pci_dma_addr;
338+
339+ if (!nents)
340+ return;
341+
342+ if (WARN_ON(!iova))
343+ return;
344+
345+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
346+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
347+
348+ /* get the nents as orig_nents given by caller */
349+ count = 0;
350+ npages = iova_size(iova);
351+ for_each_sg(sglist, sg, nents, i) {
352+ if (sg_dma_len(sg) == 0 ||
353+ sg_dma_address(sg) == DMA_MAPPING_ERROR)
354+ break;
355+
356+ npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
357+ count++;
358+ if (npages <= 0)
359+ break;
360+ }
361+
362+ /*
363+ * Before IPU6 mmu unmap, return the pci dma address back to sg
364+ * assume the nents is less than orig_nents as the least granule
365+ * is 1 SZ_4K page
366+ */
367+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", count);
368+ for_each_sg(sglist, sg, count, i) {
369+ dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg));
370+ pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
371+ sg_dma_address(sg));
372+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
373+ &pci_dma_addr, i);
374+ sg_dma_address(sg) = pci_dma_addr;
375+ }
376+
377+ dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n",
378+ iova->pfn_lo, iova->pfn_hi);
379+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
380+ PFN_PHYS(iova_size(iova)));
381+
382+ mmu->tlb_invalidate(mmu);
383+
384+ dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
385+
386+ __free_iova(&mmu->dmap->iovad, iova);
387+}
388+
389+static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist,
390+ int nents, enum dma_data_direction dir,
391+ unsigned long attrs)
392+{
393+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
394+ struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
395+ struct scatterlist *sg;
396+ struct iova *iova;
397+ size_t npages = 0;
398+ unsigned long iova_addr;
399+ int i, count;
400+
401+ for_each_sg(sglist, sg, nents, i) {
402+ if (sg->offset) {
403+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
404+ i, sg->offset);
405+ return -EFAULT;
406+ }
407+ }
408+
409+ dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents);
410+ count = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
411+ if (count <= 0) {
412+ dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents);
413+ return 0;
414+ }
415+
416+ dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count);
417+
418+ for_each_sg(sglist, sg, count, i)
419+ npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
420+
421+ iova = alloc_iova(&mmu->dmap->iovad, npages,
422+ PHYS_PFN(dma_get_mask(dev)), 0);
423+ if (!iova)
424+ return 0;
425+
426+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
427+ iova->pfn_hi);
428+
429+ iova_addr = iova->pfn_lo;
430+ for_each_sg(sglist, sg, count, i) {
431+ int ret;
432+
433+ dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n",
434+ i, PFN_PHYS(iova_addr), &sg_dma_address(sg),
435+ sg_dma_len(sg));
436+
437+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
438+ sg_dma_address(sg),
439+ PAGE_ALIGN(sg_dma_len(sg)));
440+ if (ret)
441+ goto out_fail;
442+
443+ sg_dma_address(sg) = PFN_PHYS(iova_addr);
444+
445+ iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
446+ }
447+
448+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
449+ ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
450+
451+ return count;
452+
453+out_fail:
454+ ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs);
455+
456+ return 0;
457+}
458+
459+/*
460+ * Create scatter-list for the already allocated DMA buffer
461+ */
462+static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
463+ void *cpu_addr, dma_addr_t handle, size_t size,
464+ unsigned long attrs)
465+{
466+ struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
467+ struct vm_info *info;
468+ int n_pages;
469+ int ret = 0;
470+
471+ info = get_vm_info(mmu, handle);
472+ if (!info)
473+ return -EFAULT;
474+
475+ if (!info->vaddr)
476+ return -EFAULT;
477+
478+ if (WARN_ON(!info->pages))
479+ return -ENOMEM;
480+
481+ n_pages = PHYS_PFN(PAGE_ALIGN(size));
482+
483+ ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size,
484+ GFP_KERNEL);
485+ if (ret)
486+ dev_warn(dev, "IPU6 get sgt table failed\n");
487+
488+ return ret;
489+}
490+
491+const struct dma_map_ops ipu6_dma_ops = {
492+ .alloc = ipu6_dma_alloc,
493+ .free = ipu6_dma_free,
494+ .mmap = ipu6_dma_mmap,
495+ .map_sg = ipu6_dma_map_sg,
496+ .unmap_sg = ipu6_dma_unmap_sg,
497+ .sync_single_for_cpu = ipu6_dma_sync_single_for_cpu,
498+ .sync_single_for_device = ipu6_dma_sync_single_for_cpu,
499+ .sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu,
500+ .sync_sg_for_device = ipu6_dma_sync_sg_for_cpu,
501+ .get_sgtable = ipu6_dma_get_sgtable,
502+};
drivers/media/pci/intel/ipu6/ipu6-dma.h+19−0
@@ -0,0 +1,19 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_DMA_H
5+#define IPU6_DMA_H
6+
7+#include <linux/dma-map-ops.h>
8+#include <linux/iova.h>
9+
10+struct ipu6_mmu_info;
11+
12+struct ipu6_dma_mapping {
13+ struct ipu6_mmu_info *mmu_info;
14+ struct iova_domain iovad;
15+};
16+
17+extern const struct dma_map_ops ipu6_dma_ops;
18+
19+#endif /* IPU6_DMA_H */
drivers/media/pci/intel/ipu6/ipu6-fw-com.c+413−0
@@ -0,0 +1,413 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/device.h>
7+#include <linux/dma-mapping.h>
8+#include <linux/io.h>
9+#include <linux/math.h>
10+#include <linux/overflow.h>
11+#include <linux/slab.h>
12+#include <linux/types.h>
13+
14+#include "ipu6-bus.h"
15+#include "ipu6-fw-com.h"
16+
17+/*
18+ * FWCOM layer is a shared resource between FW and driver. It consist
19+ * of token queues to both send and receive directions. Queue is simply
20+ * an array of structures with read and write indexes to the queue.
21+ * There are 1...n queues to both directions. Queues locates in
22+ * system RAM and are mapped to ISP MMU so that both CPU and ISP can
23+ * see the same buffer. Indexes are located in ISP DMEM so that FW code
24+ * can poll those with very low latency and cost. CPU access to indexes is
25+ * more costly but that happens only at message sending time and
26+ * interrupt triggered message handling. CPU doesn't need to poll indexes.
27+ * wr_reg / rd_reg are offsets to those dmem location. They are not
28+ * the indexes itself.
29+ */
30+
31+/* Shared structure between driver and FW - do not modify */
32+struct ipu6_fw_sys_queue {
33+ u64 host_address;
34+ u32 vied_address;
35+ u32 size;
36+ u32 token_size;
37+ u32 wr_reg; /* reg number in subsystem's regmem */
38+ u32 rd_reg;
39+ u32 _align;
40+} __packed;
41+
42+struct ipu6_fw_sys_queue_res {
43+ u64 host_address;
44+ u32 vied_address;
45+ u32 reg;
46+} __packed;
47+
48+enum syscom_state {
49+ /* Program load or explicit host setting should init to this */
50+ SYSCOM_STATE_UNINIT = 0x57a7e000,
51+ /* SP Syscom sets this when it is ready for use */
52+ SYSCOM_STATE_READY = 0x57a7e001,
53+ /* SP Syscom sets this when no more syscom accesses will happen */
54+ SYSCOM_STATE_INACTIVE = 0x57a7e002,
55+};
56+
57+enum syscom_cmd {
58+ /* Program load or explicit host setting should init to this */
59+ SYSCOM_COMMAND_UNINIT = 0x57a7f000,
60+ /* Host Syscom requests syscom to become inactive */
61+ SYSCOM_COMMAND_INACTIVE = 0x57a7f001,
62+};
63+
64+/* firmware config: data that sent from the host to SP via DDR */
65+/* Cell copies data into a context */
66+
67+struct ipu6_fw_syscom_config {
68+ u32 firmware_address;
69+
70+ u32 num_input_queues;
71+ u32 num_output_queues;
72+
73+ /* ISP pointers to an array of ipu6_fw_sys_queue structures */
74+ u32 input_queue;
75+ u32 output_queue;
76+
77+ /* ISYS / PSYS private data */
78+ u32 specific_addr;
79+ u32 specific_size;
80+};
81+
82+struct ipu6_fw_com_context {
83+ struct ipu6_bus_device *adev;
84+ void __iomem *dmem_addr;
85+ int (*cell_ready)(struct ipu6_bus_device *adev);
86+ void (*cell_start)(struct ipu6_bus_device *adev);
87+
88+ void *dma_buffer;
89+ dma_addr_t dma_addr;
90+ unsigned int dma_size;
91+ unsigned long attrs;
92+
93+ struct ipu6_fw_sys_queue *input_queue; /* array of host to SP queues */
94+ struct ipu6_fw_sys_queue *output_queue; /* array of SP to host */
95+
96+ u32 config_vied_addr;
97+
98+ unsigned int buttress_boot_offset;
99+ void __iomem *base_addr;
100+};
101+
102+#define FW_COM_WR_REG 0
103+#define FW_COM_RD_REG 4
104+
105+#define REGMEM_OFFSET 0
106+#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
107+
108+enum regmem_id {
109+ /* pass pkg_dir address to SPC in non-secure mode */
110+ PKG_DIR_ADDR_REG = 0,
111+ /* Tunit CFG blob for secure - provided by host.*/
112+ TUNIT_CFG_DWR_REG = 1,
113+ /* syscom commands - modified by the host */
114+ SYSCOM_COMMAND_REG = 2,
115+ /* Store interrupt status - updated by SP */
116+ SYSCOM_IRQ_REG = 3,
117+ /* first syscom queue pointer register */
118+ SYSCOM_QPR_BASE_REG = 4
119+};
120+
121+#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
122+#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id) \
123+ ((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
124+
125+enum buttress_syscom_id {
126+ /* pass syscom configuration to SPC */
127+ SYSCOM_CONFIG_ID = 0,
128+ /* syscom state - modified by SP */
129+ SYSCOM_STATE_ID = 1,
130+ /* syscom vtl0 addr mask */
131+ SYSCOM_VTL0_ADDR_MASK_ID = 2,
132+ SYSCOM_ID_MAX
133+};
134+
135+static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
136+ unsigned int token_size,
137+ struct ipu6_fw_sys_queue_res *res)
138+{
139+ unsigned int buf_size = (size + 1) * token_size;
140+
141+ q->size = size + 1;
142+ q->token_size = token_size;
143+
144+ /* acquire the shared buffer space */
145+ q->host_address = res->host_address;
146+ res->host_address += buf_size;
147+ q->vied_address = res->vied_address;
148+ res->vied_address += buf_size;
149+
150+ /* acquire the shared read and writer pointers */
151+ q->wr_reg = res->reg;
152+ res->reg++;
153+ q->rd_reg = res->reg;
154+ res->reg++;
155+}
156+
157+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
158+ struct ipu6_bus_device *adev, void __iomem *base)
159+{
160+ size_t conf_size, inq_size, outq_size, specific_size;
161+ struct ipu6_fw_syscom_config *config_host_addr;
162+ unsigned int sizeinput = 0, sizeoutput = 0;
163+ struct ipu6_fw_sys_queue_res res;
164+ struct ipu6_fw_com_context *ctx;
165+ struct device *dev = &adev->auxdev.dev;
166+ size_t sizeall, offset;
167+ unsigned long attrs = 0;
168+ void *specific_host_addr;
169+ unsigned int i;
170+
171+ if (!cfg || !cfg->cell_start || !cfg->cell_ready)
172+ return NULL;
173+
174+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
175+ if (!ctx)
176+ return NULL;
177+ ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
178+ ctx->adev = adev;
179+ ctx->cell_start = cfg->cell_start;
180+ ctx->cell_ready = cfg->cell_ready;
181+ ctx->buttress_boot_offset = cfg->buttress_boot_offset;
182+ ctx->base_addr = base;
183+
184+ /*
185+ * Allocate DMA mapped memory. Allocate one big chunk.
186+ */
187+ /* Base cfg for FW */
188+ conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
189+ /* Descriptions of the queues */
190+ inq_size = size_mul(cfg->num_input_queues,
191+ sizeof(struct ipu6_fw_sys_queue));
192+ outq_size = size_mul(cfg->num_output_queues,
193+ sizeof(struct ipu6_fw_sys_queue));
194+ /* FW specific information structure */
195+ specific_size = roundup(cfg->specific_size, 8);
196+
197+ sizeall = conf_size + inq_size + outq_size + specific_size;
198+
199+ for (i = 0; i < cfg->num_input_queues; i++)
200+ sizeinput += size_mul(cfg->input[i].queue_size + 1,
201+ cfg->input[i].token_size);
202+
203+ for (i = 0; i < cfg->num_output_queues; i++)
204+ sizeoutput += size_mul(cfg->output[i].queue_size + 1,
205+ cfg->output[i].token_size);
206+
207+ sizeall += sizeinput + sizeoutput;
208+
209+ ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr,
210+ GFP_KERNEL, attrs);
211+ ctx->attrs = attrs;
212+ if (!ctx->dma_buffer) {
213+ dev_err(dev, "failed to allocate dma memory\n");
214+ kfree(ctx);
215+ return NULL;
216+ }
217+
218+ ctx->dma_size = sizeall;
219+
220+ config_host_addr = ctx->dma_buffer;
221+ ctx->config_vied_addr = ctx->dma_addr;
222+
223+ offset = conf_size;
224+ ctx->input_queue = ctx->dma_buffer + offset;
225+ config_host_addr->input_queue = ctx->dma_addr + offset;
226+ config_host_addr->num_input_queues = cfg->num_input_queues;
227+
228+ offset += inq_size;
229+ ctx->output_queue = ctx->dma_buffer + offset;
230+ config_host_addr->output_queue = ctx->dma_addr + offset;
231+ config_host_addr->num_output_queues = cfg->num_output_queues;
232+
233+ /* copy firmware specific data */
234+ offset += outq_size;
235+ specific_host_addr = ctx->dma_buffer + offset;
236+ config_host_addr->specific_addr = ctx->dma_addr + offset;
237+ config_host_addr->specific_size = cfg->specific_size;
238+ if (cfg->specific_addr && cfg->specific_size)
239+ memcpy(specific_host_addr, cfg->specific_addr,
240+ cfg->specific_size);
241+
242+ /* initialize input queues */
243+ offset += specific_size;
244+ res.reg = SYSCOM_QPR_BASE_REG;
245+ res.host_address = (u64)(ctx->dma_buffer + offset);
246+ res.vied_address = ctx->dma_addr + offset;
247+ for (i = 0; i < cfg->num_input_queues; i++)
248+ ipu6_sys_queue_init(ctx->input_queue + i,
249+ cfg->input[i].queue_size,
250+ cfg->input[i].token_size, &res);
251+
252+ /* initialize output queues */
253+ offset += sizeinput;
254+ res.host_address = (u64)(ctx->dma_buffer + offset);
255+ res.vied_address = ctx->dma_addr + offset;
256+ for (i = 0; i < cfg->num_output_queues; i++) {
257+ ipu6_sys_queue_init(ctx->output_queue + i,
258+ cfg->output[i].queue_size,
259+ cfg->output[i].token_size, &res);
260+ }
261+
262+ return ctx;
263+}
264+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
265+
266+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
267+{
268+ /* write magic pattern to disable the tunit trace */
269+ writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4);
270+ /* Check if SP is in valid state */
271+ if (!ctx->cell_ready(ctx->adev))
272+ return -EIO;
273+
274+ /* store syscom uninitialized command */
275+ writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
276+
277+ /* store syscom uninitialized state */
278+ writel(SYSCOM_STATE_UNINIT,
279+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
280+ ctx->buttress_boot_offset,
281+ SYSCOM_STATE_ID));
282+
283+ /* store firmware configuration address */
284+ writel(ctx->config_vied_addr,
285+ BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
286+ ctx->buttress_boot_offset,
287+ SYSCOM_CONFIG_ID));
288+ ctx->cell_start(ctx->adev);
289+
290+ return 0;
291+}
292+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
293+
294+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
295+{
296+ int state;
297+
298+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
299+ ctx->buttress_boot_offset,
300+ SYSCOM_STATE_ID));
301+ if (state != SYSCOM_STATE_READY)
302+ return -EBUSY;
303+
304+ /* set close request flag */
305+ writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
306+ SYSCOM_COMMAND_REG * 4);
307+
308+ return 0;
309+}
310+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
311+
312+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
313+{
314+ /* check if release is forced, an verify cell state if it is not */
315+ if (!force && !ctx->cell_ready(ctx->adev))
316+ return -EBUSY;
317+
318+ dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size,
319+ ctx->dma_buffer, ctx->dma_addr, ctx->attrs);
320+ kfree(ctx);
321+ return 0;
322+}
323+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
324+
325+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
326+{
327+ int state;
328+
329+ state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
330+ ctx->buttress_boot_offset,
331+ SYSCOM_STATE_ID));
332+
333+ return state == SYSCOM_STATE_READY;
334+}
335+EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
336+
337+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
338+{
339+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
340+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
341+ unsigned int wr, rd;
342+ unsigned int packets;
343+ unsigned int index;
344+
345+ wr = readl(q_dmem + FW_COM_WR_REG);
346+ rd = readl(q_dmem + FW_COM_RD_REG);
347+
348+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
349+ return NULL;
350+
351+ if (wr < rd)
352+ packets = rd - wr - 1;
353+ else
354+ packets = q->size - (wr - rd + 1);
355+
356+ if (!packets)
357+ return NULL;
358+
359+ index = readl(q_dmem + FW_COM_WR_REG);
360+
361+ return (void *)(q->host_address + index * q->token_size);
362+}
363+EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
364+
365+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
366+{
367+ struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
368+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
369+ unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
370+
371+ if (wr >= q->size)
372+ wr = 0;
373+
374+ writel(wr, q_dmem + FW_COM_WR_REG);
375+}
376+EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
377+
378+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
379+{
380+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
381+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
382+ unsigned int wr, rd;
383+ unsigned int packets;
384+
385+ wr = readl(q_dmem + FW_COM_WR_REG);
386+ rd = readl(q_dmem + FW_COM_RD_REG);
387+
388+ if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
389+ return NULL;
390+
391+ if (wr < rd)
392+ wr += q->size;
393+
394+ packets = wr - rd;
395+ if (!packets)
396+ return NULL;
397+
398+ return (void *)(q->host_address + rd * q->token_size);
399+}
400+EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
401+
402+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
403+{
404+ struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
405+ void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
406+ unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1;
407+
408+ if (rd >= q->size)
409+ rd = 0;
410+
411+ writel(rd, q_dmem + FW_COM_RD_REG);
412+}
413+EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
drivers/media/pci/intel/ipu6/ipu6-fw-com.h+47−0
@@ -0,0 +1,47 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_FW_COM_H
5+#define IPU6_FW_COM_H
6+
7+struct ipu6_fw_com_context;
8+struct ipu6_bus_device;
9+
10+struct ipu6_fw_syscom_queue_config {
11+ unsigned int queue_size; /* tokens per queue */
12+ unsigned int token_size; /* bytes per token */
13+};
14+
15+#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET 0
16+
17+struct ipu6_fw_com_cfg {
18+ unsigned int num_input_queues;
19+ unsigned int num_output_queues;
20+ struct ipu6_fw_syscom_queue_config *input;
21+ struct ipu6_fw_syscom_queue_config *output;
22+
23+ unsigned int dmem_addr;
24+
25+ /* firmware-specific configuration data */
26+ void *specific_addr;
27+ unsigned int specific_size;
28+ int (*cell_ready)(struct ipu6_bus_device *adev);
29+ void (*cell_start)(struct ipu6_bus_device *adev);
30+
31+ unsigned int buttress_boot_offset;
32+};
33+
34+void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
35+ struct ipu6_bus_device *adev, void __iomem *base);
36+
37+int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx);
38+bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx);
39+int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx);
40+int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force);
41+
42+void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
43+void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
44+void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
45+void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
46+
47+#endif
drivers/media/pci/intel/ipu6/ipu6-fw-isys.c+487−0
@@ -0,0 +1,487 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/cacheflush.h>
7+#include <linux/delay.h>
8+#include <linux/device.h>
9+#include <linux/io.h>
10+#include <linux/spinlock.h>
11+#include <linux/types.h>
12+
13+#include "ipu6-bus.h"
14+#include "ipu6-fw-com.h"
15+#include "ipu6-isys.h"
16+#include "ipu6-platform-isys-csi2-reg.h"
17+#include "ipu6-platform-regs.h"
18+
19+static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {
20+ "STREAM_OPEN",
21+ "STREAM_START",
22+ "STREAM_START_AND_CAPTURE",
23+ "STREAM_CAPTURE",
24+ "STREAM_STOP",
25+ "STREAM_FLUSH",
26+ "STREAM_CLOSE"
27+};
28+
29+static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
30+{
31+ struct device *dev = &isys->adev->auxdev.dev;
32+ struct ipu6_fw_isys_proxy_resp_info_abi *resp;
33+ int ret;
34+
35+ resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
36+ if (!resp)
37+ return 1;
38+
39+ dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n",
40+ resp->request_id, resp->error_info.error,
41+ resp->error_info.error_details);
42+
43+ ret = req_id == resp->request_id ? 0 : -EIO;
44+
45+ ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
46+
47+ return ret;
48+}
49+
50+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
51+ unsigned int req_id,
52+ unsigned int index,
53+ unsigned int offset, u32 value)
54+{
55+ struct ipu6_fw_com_context *ctx = isys->fwcom;
56+ struct device *dev = &isys->adev->auxdev.dev;
57+ struct ipu6_fw_proxy_send_queue_token *token;
58+ unsigned int timeout = 1000;
59+ int ret;
60+
61+ dev_dbg(dev,
62+ "proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n",
63+ req_id, index, offset, value);
64+
65+ token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
66+ if (!token)
67+ return -EBUSY;
68+
69+ token->request_id = req_id;
70+ token->region_index = index;
71+ token->offset = offset;
72+ token->value = value;
73+ ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
74+
75+ do {
76+ usleep_range(100, 110);
77+ ret = handle_proxy_response(isys, req_id);
78+ if (!ret)
79+ break;
80+ if (ret == -EIO) {
81+ dev_err(dev, "Proxy respond with unexpected id\n");
82+ break;
83+ }
84+ timeout--;
85+ } while (ret && timeout);
86+
87+ if (!timeout)
88+ dev_err(dev, "Proxy response timed out\n");
89+
90+ return ret;
91+}
92+
93+int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
94+ const unsigned int stream_handle,
95+ void *cpu_mapped_buf,
96+ dma_addr_t dma_mapped_buf,
97+ size_t size, u16 send_type)
98+{
99+ struct ipu6_fw_com_context *ctx = isys->fwcom;
100+ struct device *dev = &isys->adev->auxdev.dev;
101+ struct ipu6_fw_send_queue_token *token;
102+
103+ if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE)
104+ return -EINVAL;
105+
106+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
107+
108+ /*
109+ * Time to flush cache in case we have some payload. Not all messages
110+ * have that
111+ */
112+ if (cpu_mapped_buf)
113+ clflush_cache_range(cpu_mapped_buf, size);
114+
115+ token = ipu6_send_get_token(ctx,
116+ stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
117+ if (!token)
118+ return -EBUSY;
119+
120+ token->payload = dma_mapped_buf;
121+ token->buf_handle = (unsigned long)cpu_mapped_buf;
122+ token->send_type = send_type;
123+
124+ ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
125+
126+ return 0;
127+}
128+
129+int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
130+ const unsigned int stream_handle, u16 send_type)
131+{
132+ return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
133+ send_type);
134+}
135+
136+int ipu6_fw_isys_close(struct ipu6_isys *isys)
137+{
138+ struct device *dev = &isys->adev->auxdev.dev;
139+ int retry = IPU6_ISYS_CLOSE_RETRY;
140+ unsigned long flags;
141+ void *fwcom;
142+ int ret;
143+
144+ /*
145+ * Stop the isys fw. Actual close takes
146+ * some time as the FW must stop its actions including code fetch
147+ * to SP icache.
148+ * spinlock to wait the interrupt handler to be finished
149+ */
150+ spin_lock_irqsave(&isys->power_lock, flags);
151+ ret = ipu6_fw_com_close(isys->fwcom);
152+ fwcom = isys->fwcom;
153+ isys->fwcom = NULL;
154+ spin_unlock_irqrestore(&isys->power_lock, flags);
155+ if (ret)
156+ dev_err(dev, "Device close failure: %d\n", ret);
157+
158+ /* release probably fails if the close failed. Let's try still */
159+ do {
160+ usleep_range(400, 500);
161+ ret = ipu6_fw_com_release(fwcom, 0);
162+ retry--;
163+ } while (ret && retry);
164+
165+ if (ret) {
166+ dev_err(dev, "Device release time out %d\n", ret);
167+ spin_lock_irqsave(&isys->power_lock, flags);
168+ isys->fwcom = fwcom;
169+ spin_unlock_irqrestore(&isys->power_lock, flags);
170+ }
171+
172+ return ret;
173+}
174+
175+void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
176+{
177+ int ret;
178+
179+ ret = ipu6_fw_com_release(isys->fwcom, 1);
180+ if (ret < 0)
181+ dev_warn(&isys->adev->auxdev.dev,
182+ "Device busy, fw_com release failed.");
183+ isys->fwcom = NULL;
184+}
185+
186+static void start_sp(struct ipu6_bus_device *adev)
187+{
188+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
189+ void __iomem *spc_regs_base = isys->pdata->base +
190+ isys->pdata->ipdata->hw_variant.spc_offset;
191+ u32 val = IPU6_ISYS_SPC_STATUS_START |
192+ IPU6_ISYS_SPC_STATUS_RUN |
193+ IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
194+
195+ val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0;
196+
197+ writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
198+}
199+
200+static int query_sp(struct ipu6_bus_device *adev)
201+{
202+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
203+ void __iomem *spc_regs_base = isys->pdata->base +
204+ isys->pdata->ipdata->hw_variant.spc_offset;
205+ u32 val;
206+
207+ val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
208+ /* return true when READY == 1, START == 0 */
209+ val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START;
210+
211+ return val == IPU6_ISYS_SPC_STATUS_READY;
212+}
213+
214+static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
215+ struct ipu6_fw_com_cfg *fwcom,
216+ unsigned int num_streams)
217+{
218+ unsigned int max_send_queues, max_sram_blocks, max_devq_size;
219+ struct ipu6_fw_syscom_queue_config *input_queue_cfg;
220+ struct ipu6_fw_syscom_queue_config *output_queue_cfg;
221+ struct device *dev = &isys->adev->auxdev.dev;
222+ int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY;
223+ int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV;
224+ int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG;
225+ int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES;
226+ int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES;
227+ int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES;
228+ struct ipu6_fw_isys_fw_config *isys_fw_cfg;
229+ u32 num_in_message_queues;
230+ unsigned int max_streams;
231+ unsigned int size;
232+ unsigned int i;
233+
234+ max_streams = isys->pdata->ipdata->max_streams;
235+ max_send_queues = isys->pdata->ipdata->max_send_queues;
236+ max_sram_blocks = isys->pdata->ipdata->max_sram_blocks;
237+ max_devq_size = isys->pdata->ipdata->max_devq_size;
238+ num_in_message_queues = clamp(num_streams, 1U, max_streams);
239+ isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL);
240+ if (!isys_fw_cfg)
241+ return -ENOMEM;
242+
243+ isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES;
244+ isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES;
245+ isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues;
246+ isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES;
247+ /* Common msg/dev return queue */
248+ isys_fw_cfg->num_recv_queues[type_dev] = 0;
249+ isys_fw_cfg->num_recv_queues[type_msg] = 1;
250+
251+ size = sizeof(*input_queue_cfg) * max_send_queues;
252+ input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
253+ if (!input_queue_cfg)
254+ return -ENOMEM;
255+
256+ size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES;
257+ output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
258+ if (!output_queue_cfg)
259+ return -ENOMEM;
260+
261+ fwcom->input = input_queue_cfg;
262+ fwcom->output = output_queue_cfg;
263+
264+ fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] +
265+ isys_fw_cfg->num_send_queues[type_dev] +
266+ isys_fw_cfg->num_send_queues[type_msg];
267+
268+ fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] +
269+ isys_fw_cfg->num_recv_queues[type_dev] +
270+ isys_fw_cfg->num_recv_queues[type_msg];
271+
272+ /* SRAM partitioning. Equal partitioning is set. */
273+ for (i = 0; i < max_sram_blocks; i++) {
274+ if (i < num_in_message_queues)
275+ isys_fw_cfg->buffer_partition.num_gda_pages[i] =
276+ (IPU6_DEVICE_GDA_NR_PAGES *
277+ IPU6_DEVICE_GDA_VIRT_FACTOR) /
278+ num_in_message_queues;
279+ else
280+ isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;
281+ }
282+
283+ /* FW assumes proxy interface at fwcom queue 0 */
284+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) {
285+ input_queue_cfg[i].token_size =
286+ sizeof(struct ipu6_fw_proxy_send_queue_token);
287+ input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE;
288+ }
289+
290+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) {
291+ input_queue_cfg[base_dev_send + i].token_size =
292+ sizeof(struct ipu6_fw_send_queue_token);
293+ input_queue_cfg[base_dev_send + i].queue_size = max_devq_size;
294+ }
295+
296+ for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) {
297+ input_queue_cfg[base_msg_send + i].token_size =
298+ sizeof(struct ipu6_fw_send_queue_token);
299+ input_queue_cfg[base_msg_send + i].queue_size =
300+ IPU6_ISYS_SIZE_SEND_QUEUE;
301+ }
302+
303+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) {
304+ output_queue_cfg[i].token_size =
305+ sizeof(struct ipu6_fw_proxy_resp_queue_token);
306+ output_queue_cfg[i].queue_size =
307+ IPU6_ISYS_SIZE_PROXY_RECV_QUEUE;
308+ }
309+ /* There is no recv DEV queue */
310+ for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) {
311+ output_queue_cfg[base_msg_recv + i].token_size =
312+ sizeof(struct ipu6_fw_resp_queue_token);
313+ output_queue_cfg[base_msg_recv + i].queue_size =
314+ IPU6_ISYS_SIZE_RECV_QUEUE;
315+ }
316+
317+ fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;
318+ fwcom->specific_addr = isys_fw_cfg;
319+ fwcom->specific_size = sizeof(*isys_fw_cfg);
320+
321+ return 0;
322+}
323+
324+int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
325+{
326+ struct device *dev = &isys->adev->auxdev.dev;
327+ int retry = IPU6_ISYS_OPEN_RETRY;
328+ struct ipu6_fw_com_cfg fwcom = {
329+ .cell_start = start_sp,
330+ .cell_ready = query_sp,
331+ .buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET,
332+ };
333+ int ret;
334+
335+ ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams);
336+
337+ isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,
338+ isys->pdata->base);
339+ if (!isys->fwcom) {
340+ dev_err(dev, "isys fw com prepare failed\n");
341+ return -EIO;
342+ }
343+
344+ ret = ipu6_fw_com_open(isys->fwcom);
345+ if (ret) {
346+ dev_err(dev, "isys fw com open failed %d\n", ret);
347+ return ret;
348+ }
349+
350+ do {
351+ usleep_range(400, 500);
352+ if (ipu6_fw_com_ready(isys->fwcom))
353+ break;
354+ retry--;
355+ } while (retry > 0);
356+
357+ if (!retry) {
358+ dev_err(dev, "isys port open ready failed %d\n", ret);
359+ ipu6_fw_isys_close(isys);
360+ ret = -EIO;
361+ }
362+
363+ return ret;
364+}
365+
366+struct ipu6_fw_isys_resp_info_abi *
367+ipu6_fw_isys_get_resp(void *context, unsigned int queue)
368+{
369+ return ipu6_recv_get_token(context, queue);
370+}
371+
372+void ipu6_fw_isys_put_resp(void *context, unsigned int queue)
373+{
374+ ipu6_recv_put_token(context, queue);
375+}
376+
377+void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
378+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
379+{
380+ unsigned int i;
381+
382+ dev_dbg(dev, "-----------------------------------------------------\n");
383+ dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n");
384+
385+ dev_dbg(dev, "compfmt = %d\n", cfg->vc);
386+ dev_dbg(dev, "src = %d\n", cfg->src);
387+ dev_dbg(dev, "vc = %d\n", cfg->vc);
388+ dev_dbg(dev, "isl_use = %d\n", cfg->isl_use);
389+ dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type);
390+
391+ dev_dbg(dev, "send_irq_sof_discarded = %d\n",
392+ cfg->send_irq_sof_discarded);
393+ dev_dbg(dev, "send_irq_eof_discarded = %d\n",
394+ cfg->send_irq_eof_discarded);
395+ dev_dbg(dev, "send_resp_sof_discarded = %d\n",
396+ cfg->send_resp_sof_discarded);
397+ dev_dbg(dev, "send_resp_eof_discarded = %d\n",
398+ cfg->send_resp_eof_discarded);
399+
400+ dev_dbg(dev, "crop:\n");
401+ dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset,
402+ cfg->crop.top_offset);
403+ dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset,
404+ cfg->crop.bottom_offset);
405+
406+ dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins);
407+ for (i = 0; i < cfg->nof_input_pins; i++) {
408+ dev_dbg(dev, "input pin[%d]:\n", i);
409+ dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt);
410+ dev_dbg(dev, "\t.mipi_store_mode = %d\n",
411+ cfg->input_pins[i].mipi_store_mode);
412+ dev_dbg(dev, "\t.bits_per_pix = %d\n",
413+ cfg->input_pins[i].bits_per_pix);
414+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
415+ cfg->input_pins[i].mapped_dt);
416+ dev_dbg(dev, "\t.input_res = %dx%d\n",
417+ cfg->input_pins[i].input_res.width,
418+ cfg->input_pins[i].input_res.height);
419+ dev_dbg(dev, "\t.mipi_decompression = %d\n",
420+ cfg->input_pins[i].mipi_decompression);
421+ dev_dbg(dev, "\t.capture_mode = %d\n",
422+ cfg->input_pins[i].capture_mode);
423+ }
424+
425+ dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins);
426+ for (i = 0; i < cfg->nof_output_pins; i++) {
427+ dev_dbg(dev, "output_pin[%d]:\n", i);
428+ dev_dbg(dev, "\t.input_pin_id = %d\n",
429+ cfg->output_pins[i].input_pin_id);
430+ dev_dbg(dev, "\t.output_res = %dx%d\n",
431+ cfg->output_pins[i].output_res.width,
432+ cfg->output_pins[i].output_res.height);
433+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
434+ dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt);
435+ dev_dbg(dev, "\t.payload_buf_size = %d\n",
436+ cfg->output_pins[i].payload_buf_size);
437+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
438+ dev_dbg(dev, "\t.watermark_in_lines = %d\n",
439+ cfg->output_pins[i].watermark_in_lines);
440+ dev_dbg(dev, "\t.send_irq = %d\n",
441+ cfg->output_pins[i].send_irq);
442+ dev_dbg(dev, "\t.reserve_compression = %d\n",
443+ cfg->output_pins[i].reserve_compression);
444+ dev_dbg(dev, "\t.snoopable = %d\n",
445+ cfg->output_pins[i].snoopable);
446+ dev_dbg(dev, "\t.error_handling_enable = %d\n",
447+ cfg->output_pins[i].error_handling_enable);
448+ dev_dbg(dev, "\t.sensor_type = %d\n",
449+ cfg->output_pins[i].sensor_type);
450+ }
451+ dev_dbg(dev, "-----------------------------------------------------\n");
452+}
453+
454+void
455+ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
456+ struct ipu6_fw_isys_frame_buff_set_abi *buf,
457+ unsigned int outputs)
458+{
459+ unsigned int i;
460+
461+ dev_dbg(dev, "-----------------------------------------------------\n");
462+ dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");
463+
464+ for (i = 0; i < outputs; i++) {
465+ dev_dbg(dev, "output_pin[%d]:\n", i);
466+ dev_dbg(dev, "\t.out_buf_id = %llu\n",
467+ buf->output_pins[i].out_buf_id);
468+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
469+ dev_dbg(dev, "\t.compress = %d\n",
470+ buf->output_pins[i].compress);
471+ }
472+
473+ dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof);
474+ dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof);
475+ dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof);
476+ dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof);
477+ dev_dbg(dev, "send_irq_capture_ack = 0x%x\n",
478+ buf->send_irq_capture_ack);
479+ dev_dbg(dev, "send_irq_capture_done = 0x%x\n",
480+ buf->send_irq_capture_done);
481+ dev_dbg(dev, "send_resp_capture_ack = 0x%x\n",
482+ buf->send_resp_capture_ack);
483+ dev_dbg(dev, "send_resp_capture_done = 0x%x\n",
484+ buf->send_resp_capture_done);
485+
486+ dev_dbg(dev, "-----------------------------------------------------\n");
487+}
drivers/media/pci/intel/ipu6/ipu6-fw-isys.h+596−0
@@ -0,0 +1,596 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_FW_ISYS_H
5+#define IPU6_FW_ISYS_H
6+
7+#include <linux/types.h>
8+
9+struct device;
10+struct ipu6_isys;
11+
12+/* Max number of Input/Output Pins */
13+#define IPU6_MAX_IPINS 4
14+
15+#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1)
16+
17+#define IPU6_STREAM_ID_MAX 16
18+#define IPU6_NONSECURE_STREAM_ID_MAX 12
19+#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX)
20+#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX)
21+#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX)
22+#define IPU6SE_STREAM_ID_MAX 8
23+#define IPU6SE_NONSECURE_STREAM_ID_MAX 4
24+#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX)
25+#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX)
26+#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX)
27+
28+/* Single return queue for all streams/commands type */
29+#define IPU6_N_MAX_MSG_RECV_QUEUES 1
30+/* Single device queue for high priority commands (bypass in-order queue) */
31+#define IPU6_N_MAX_DEV_SEND_QUEUES 1
32+/* Single dedicated send queue for proxy interface */
33+#define IPU6_N_MAX_PROXY_SEND_QUEUES 1
34+/* Single dedicated recv queue for proxy interface */
35+#define IPU6_N_MAX_PROXY_RECV_QUEUES 1
36+/* Send queues layout */
37+#define IPU6_BASE_PROXY_SEND_QUEUES 0
38+#define IPU6_BASE_DEV_SEND_QUEUES \
39+ (IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES)
40+#define IPU6_BASE_MSG_SEND_QUEUES \
41+ (IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES)
42+/* Recv queues layout */
43+#define IPU6_BASE_PROXY_RECV_QUEUES 0
44+#define IPU6_BASE_MSG_RECV_QUEUES \
45+ (IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES)
46+#define IPU6_N_MAX_RECV_QUEUES \
47+ (IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES)
48+
49+#define IPU6_N_MAX_SEND_QUEUES \
50+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES)
51+#define IPU6SE_N_MAX_SEND_QUEUES \
52+ (IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES)
53+
54+/* Max number of planes for frame formats supported by the FW */
55+#define IPU6_PIN_PLANES_MAX 4
56+
57+#define IPU6_FW_ISYS_SENSOR_TYPE_START 14
58+#define IPU6_FW_ISYS_SENSOR_TYPE_END 19
59+#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6
60+#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11
61+/*
62+ * Device close takes some time from last ack message to actual stopping
63+ * of the SP processor. As long as the SP processor runs we can't proceed with
64+ * clean up of resources.
65+ */
66+#define IPU6_ISYS_OPEN_RETRY 2000
67+#define IPU6_ISYS_CLOSE_RETRY 2000
68+#define IPU6_FW_CALL_TIMEOUT_JIFFIES msecs_to_jiffies(2000)
69+
70+enum ipu6_fw_isys_resp_type {
71+ IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
72+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK,
73+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
74+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK,
75+ IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK,
76+ IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK,
77+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK,
78+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY,
79+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK,
80+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF,
81+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF,
82+ IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
83+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE,
84+ IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED,
85+ IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED,
86+ IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED,
87+ IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED,
88+ IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY,
89+ N_IPU6_FW_ISYS_RESP_TYPE
90+};
91+
92+enum ipu6_fw_isys_send_type {
93+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0,
94+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START,
95+ IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE,
96+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE,
97+ IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP,
98+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH,
99+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE,
100+ N_IPU6_FW_ISYS_SEND_TYPE
101+};
102+
103+enum ipu6_fw_isys_queue_type {
104+ IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0,
105+ IPU6_FW_ISYS_QUEUE_TYPE_DEV,
106+ IPU6_FW_ISYS_QUEUE_TYPE_MSG,
107+ N_IPU6_FW_ISYS_QUEUE_TYPE
108+};
109+
110+enum ipu6_fw_isys_stream_source {
111+ IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0,
112+ IPU6_FW_ISYS_STREAM_SRC_PORT_1,
113+ IPU6_FW_ISYS_STREAM_SRC_PORT_2,
114+ IPU6_FW_ISYS_STREAM_SRC_PORT_3,
115+ IPU6_FW_ISYS_STREAM_SRC_PORT_4,
116+ IPU6_FW_ISYS_STREAM_SRC_PORT_5,
117+ IPU6_FW_ISYS_STREAM_SRC_PORT_6,
118+ IPU6_FW_ISYS_STREAM_SRC_PORT_7,
119+ IPU6_FW_ISYS_STREAM_SRC_PORT_8,
120+ IPU6_FW_ISYS_STREAM_SRC_PORT_9,
121+ IPU6_FW_ISYS_STREAM_SRC_PORT_10,
122+ IPU6_FW_ISYS_STREAM_SRC_PORT_11,
123+ IPU6_FW_ISYS_STREAM_SRC_PORT_12,
124+ IPU6_FW_ISYS_STREAM_SRC_PORT_13,
125+ IPU6_FW_ISYS_STREAM_SRC_PORT_14,
126+ IPU6_FW_ISYS_STREAM_SRC_PORT_15,
127+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0,
128+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1,
129+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2,
130+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3,
131+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4,
132+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5,
133+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6,
134+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7,
135+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8,
136+ IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9,
137+ N_IPU6_FW_ISYS_STREAM_SRC
138+};
139+
140+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0
141+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1
142+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2
143+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3
144+
145+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4
146+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5
147+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \
148+ IPU6_FW_ISYS_STREAM_SRC_PORT_6
149+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \
150+ IPU6_FW_ISYS_STREAM_SRC_PORT_7
151+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \
152+ IPU6_FW_ISYS_STREAM_SRC_PORT_8
153+#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \
154+ IPU6_FW_ISYS_STREAM_SRC_PORT_9
155+
156+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0
157+#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1
158+
159+/*
160+ * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec
161+ * supports up to 4 virtual per physical channel
162+ */
163+enum ipu6_fw_isys_mipi_vc {
164+ IPU6_FW_ISYS_MIPI_VC_0 = 0,
165+ IPU6_FW_ISYS_MIPI_VC_1,
166+ IPU6_FW_ISYS_MIPI_VC_2,
167+ IPU6_FW_ISYS_MIPI_VC_3,
168+ N_IPU6_FW_ISYS_MIPI_VC
169+};
170+
171+enum ipu6_fw_isys_frame_format_type {
172+ IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */
173+ IPU6_FW_ISYS_FRAME_FORMAT_NV12, /* 12 bit YUV 420, Y, UV plane */
174+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */
175+ /* 12 bit YUV 420, Intel proprietary tiled format */
176+ IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY,
177+
178+ IPU6_FW_ISYS_FRAME_FORMAT_NV16, /* 16 bit YUV 422, Y, UV plane */
179+ IPU6_FW_ISYS_FRAME_FORMAT_NV21, /* 12 bit YUV 420, Y, VU plane */
180+ IPU6_FW_ISYS_FRAME_FORMAT_NV61, /* 16 bit YUV 422, Y, VU plane */
181+ IPU6_FW_ISYS_FRAME_FORMAT_YV12, /* 12 bit YUV 420, Y, V, U plane */
182+ IPU6_FW_ISYS_FRAME_FORMAT_YV16, /* 16 bit YUV 422, Y, V, U plane */
183+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */
184+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */
185+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */
186+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */
187+ IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */
188+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */
189+ IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */
190+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY, /* 16 bit YUV 422, UYVY interleaved */
191+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV, /* 16 bit YUV 422, YUYV interleaved */
192+ IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */
193+ /* Internal format, 2 y lines followed by a uvinterleaved line */
194+ IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE,
195+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8, /* RAW8, 1 plane */
196+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */
197+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */
198+ IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */
199+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */
200+ /**
201+ * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit
202+ * value, 5 bits for R, 6 bits for G and 5 bits for B.
203+ */
204+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565,
205+ IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */
206+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */
207+ IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */
208+ IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */
209+ N_IPU6_FW_ISYS_FRAME_FORMAT
210+};
211+
212+enum ipu6_fw_isys_pin_type {
213+ /* captured as MIPI packets */
214+ IPU6_FW_ISYS_PIN_TYPE_MIPI = 0,
215+ /* captured through the SoC path */
216+ IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3,
217+};
218+
219+/*
220+ * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach
221+ * MIPI SRAM with the long packet header or
222+ * if not, then only option is to capture it with pin type MIPI.
223+ */
224+enum ipu6_fw_isys_mipi_store_mode {
225+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0,
226+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER,
227+ N_IPU6_FW_ISYS_MIPI_STORE_MODE
228+};
229+
230+enum ipu6_fw_isys_capture_mode {
231+ IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0,
232+ IPU6_FW_ISYS_CAPTURE_MODE_BURST,
233+ N_IPU6_FW_ISYS_CAPTURE_MODE,
234+};
235+
236+enum ipu6_fw_isys_sensor_mode {
237+ IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0,
238+ IPU6_FW_ISYS_SENSOR_MODE_TOBII,
239+ N_IPU6_FW_ISYS_SENSOR_MODE,
240+};
241+
242+enum ipu6_fw_isys_error {
243+ IPU6_FW_ISYS_ERROR_NONE = 0,
244+ IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY,
245+ IPU6_FW_ISYS_ERROR_HW_CONSISTENCY,
246+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE,
247+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION,
248+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION,
249+ IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION,
250+ IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES,
251+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO,
252+ IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO,
253+ IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC,
254+ IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION,
255+ IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL,
256+ N_IPU6_FW_ISYS_ERROR
257+};
258+
259+enum ipu6_fw_proxy_error {
260+ IPU6_FW_PROXY_ERROR_NONE = 0,
261+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION,
262+ IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET,
263+ N_IPU6_FW_PROXY_ERROR
264+};
265+
266+/* firmware ABI structure below are aligned in firmware, no need pack */
267+struct ipu6_fw_isys_buffer_partition_abi {
268+ u32 num_gda_pages[IPU6_STREAM_ID_MAX];
269+};
270+
271+struct ipu6_fw_isys_fw_config {
272+ struct ipu6_fw_isys_buffer_partition_abi buffer_partition;
273+ u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
274+ u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
275+};
276+
277+/*
278+ * struct ipu6_fw_isys_resolution_abi: Generic resolution structure.
279+ */
280+struct ipu6_fw_isys_resolution_abi {
281+ u32 width;
282+ u32 height;
283+};
284+
285+/**
286+ * struct ipu6_fw_isys_output_pin_payload_abi - ISYS output pin buffer
287+ * @out_buf_id: Points to output pin buffer - buffer identifier
288+ * @addr: Points to output pin buffer - CSS Virtual Address
289+ * @compress: Request frame compression (1), or not (0)
290+ */
291+struct ipu6_fw_isys_output_pin_payload_abi {
292+ u64 out_buf_id;
293+ u32 addr;
294+ u32 compress;
295+};
296+
297+/**
298+ * struct ipu6_fw_isys_output_pin_info_abi - ISYS output pin info
299+ * @output_res: output pin resolution
300+ * @stride: output stride in Bytes (not valid for statistics)
301+ * @watermark_in_lines: pin watermark level in lines
302+ * @payload_buf_size: minimum size in Bytes of all buffers that will be
303+ * supplied for capture on this pin
304+ * @ts_offsets: ts_offsets
305+ * @s2m_pixel_soc_pixel_remapping: pixel soc remapping (see the definition of
306+ * S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING)
307+ * @csi_be_soc_pixel_remapping: see s2m_pixel_soc_pixel_remapping
308+ * @send_irq: assert if pin event should trigger irq
309+ * @input_pin_id: related input pin id
310+ * @pt: pin type -real format "enum ipu6_fw_isys_pin_type"
311+ * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type"
312+ * @reserved: a reserved field
313+ * @reserve_compression: reserve compression resources for pin
314+ * @snoopable: snoopable
315+ * @error_handling_enable: enable error handling
316+ * @sensor_type: sensor_type
317+ */
318+struct ipu6_fw_isys_output_pin_info_abi {
319+ struct ipu6_fw_isys_resolution_abi output_res;
320+ u32 stride;
321+ u32 watermark_in_lines;
322+ u32 payload_buf_size;
323+ u32 ts_offsets[IPU6_PIN_PLANES_MAX];
324+ u32 s2m_pixel_soc_pixel_remapping;
325+ u32 csi_be_soc_pixel_remapping;
326+ u8 send_irq;
327+ u8 input_pin_id;
328+ u8 pt;
329+ u8 ft;
330+ u8 reserved;
331+ u8 reserve_compression;
332+ u8 snoopable;
333+ u8 error_handling_enable;
334+ u32 sensor_type;
335+};
336+
337+/**
338+ * struct ipu6_fw_isys_input_pin_info_abi - ISYS input pin info
339+ * @input_res: input resolution
340+ * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type)
341+ * @mipi_store_mode: defines if legacy long packet header will be stored or
342+ * discarded if discarded, output pin type for this
343+ * input pin can only be MIPI
344+ * (enum ipu6_fw_isys_mipi_store_mode)
345+ * @bits_per_pix: native bits per pixel
346+ * @mapped_dt: actual data type from sensor
347+ * @mipi_decompression: defines which compression will be in mipi backend
348+ * @crop_first_and_last_lines: Control whether to crop the first and last line
349+ * of the input image. Crop done by HW device.
350+ * @capture_mode: mode of capture, regular or burst, default value is regular
351+ * @reserved: a reserved field
352+ */
353+struct ipu6_fw_isys_input_pin_info_abi {
354+ struct ipu6_fw_isys_resolution_abi input_res;
355+ u8 dt;
356+ u8 mipi_store_mode;
357+ u8 bits_per_pix;
358+ u8 mapped_dt;
359+ u8 mipi_decompression;
360+ u8 crop_first_and_last_lines;
361+ u8 capture_mode;
362+ u8 reserved;
363+};
364+
365+/**
366+ * struct ipu6_fw_isys_cropping_abi - ISYS cropping coordinates
367+ * @top_offset: Top offset
368+ * @left_offset: Left offset
369+ * @bottom_offset: Bottom offset
370+ * @right_offset: Right offset
371+ */
372+struct ipu6_fw_isys_cropping_abi {
373+ s32 top_offset;
374+ s32 left_offset;
375+ s32 bottom_offset;
376+ s32 right_offset;
377+};
378+
379+/**
380+ * struct ipu6_fw_isys_stream_cfg_data_abi - ISYS stream configuration data
381+ * ISYS stream configuration data structure
382+ * @crop: for extended use and is not used in FW currently
383+ * @input_pins: input pin descriptors
384+ * @output_pins: output pin descriptors
385+ * @compfmt: de-compression setting for User Defined Data
386+ * @nof_input_pins: number of input pins
387+ * @nof_output_pins: number of output pins
388+ * @send_irq_sof_discarded: send irq on discarded frame sof response
389+ * - if '1' it will override the send_resp_sof_discarded
390+ * and send the response
391+ * - if '0' the send_resp_sof_discarded will determine
392+ * whether to send the response
393+ * @send_irq_eof_discarded: send irq on discarded frame eof response
394+ * - if '1' it will override the send_resp_eof_discarded
395+ * and send the response
396+ * - if '0' the send_resp_eof_discarded will determine
397+ * whether to send the response
398+ * @send_resp_sof_discarded: send response for discarded frame sof detected,
399+ * used only when send_irq_sof_discarded is '0'
400+ * @send_resp_eof_discarded: send response for discarded frame eof detected,
401+ * used only when send_irq_eof_discarded is '0'
402+ * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1
403+ * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel)
404+ * @isl_use: indicates whether stream requires ISL and how
405+ * @sensor_type: type of connected sensor, tobii or others, default is 0
406+ * @reserved: a reserved field
407+ * @reserved2: a reserved field
408+ */
409+struct ipu6_fw_isys_stream_cfg_data_abi {
410+ struct ipu6_fw_isys_cropping_abi crop;
411+ struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS];
412+ struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS];
413+ u32 compfmt;
414+ u8 nof_input_pins;
415+ u8 nof_output_pins;
416+ u8 send_irq_sof_discarded;
417+ u8 send_irq_eof_discarded;
418+ u8 send_resp_sof_discarded;
419+ u8 send_resp_eof_discarded;
420+ u8 src;
421+ u8 vc;
422+ u8 isl_use;
423+ u8 sensor_type;
424+ u8 reserved;
425+ u8 reserved2;
426+};
427+
428+/**
429+ * struct ipu6_fw_isys_frame_buff_set_abi - ISYS frame buffer set (request)
430+ * @output_pins: output pin addresses
431+ * @send_irq_sof: send irq on frame sof response
432+ * - if '1' it will override the send_resp_sof and
433+ * send the response
434+ * - if '0' the send_resp_sof will determine whether to
435+ * send the response
436+ * @send_irq_eof: send irq on frame eof response
437+ * - if '1' it will override the send_resp_eof and
438+ * send the response
439+ * - if '0' the send_resp_eof will determine whether to
440+ * send the response
441+ * @send_irq_capture_ack: send irq on capture ack
442+ * @send_irq_capture_done: send irq on capture done
443+ * @send_resp_sof: send response for frame sof detected,
444+ * used only when send_irq_sof is '0'
445+ * @send_resp_eof: send response for frame eof detected,
446+ * used only when send_irq_eof is '0'
447+ * @send_resp_capture_ack: send response for capture ack event
448+ * @send_resp_capture_done: send response for capture done event
449+ * @reserved: a reserved field
450+ */
451+struct ipu6_fw_isys_frame_buff_set_abi {
452+ struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS];
453+ u8 send_irq_sof;
454+ u8 send_irq_eof;
455+ u8 send_irq_capture_ack;
456+ u8 send_irq_capture_done;
457+ u8 send_resp_sof;
458+ u8 send_resp_eof;
459+ u8 send_resp_capture_ack;
460+ u8 send_resp_capture_done;
461+ u8 reserved[8];
462+};
463+
464+/**
465+ * struct ipu6_fw_isys_error_info_abi - ISYS error information
466+ * @error: error code if something went wrong
467+ * @error_details: depending on error code, it may contain additional error info
468+ */
469+struct ipu6_fw_isys_error_info_abi {
470+ u32 error;
471+ u32 error_details;
472+};
473+
474+/**
475+ * struct ipu6_fw_isys_resp_info_abi - ISYS firmware response
476+ * @buf_id: buffer ID
477+ * @pin: this var is only valid for pin event related responses,
478+ * contains pin addresses
479+ * @error_info: error information from the FW
480+ * @timestamp: Time information for event if available
481+ * @stream_handle: stream id the response corresponds to
482+ * @type: response type (enum ipu6_fw_isys_resp_type)
483+ * @pin_id: pin id that the pin payload corresponds to
484+ * @reserved: a reserved field
485+ * @reserved2: a reserved field
486+ */
487+struct ipu6_fw_isys_resp_info_abi {
488+ u64 buf_id;
489+ struct ipu6_fw_isys_output_pin_payload_abi pin;
490+ struct ipu6_fw_isys_error_info_abi error_info;
491+ u32 timestamp[2];
492+ u8 stream_handle;
493+ u8 type;
494+ u8 pin_id;
495+ u8 reserved;
496+ u32 reserved2;
497+};
498+
499+/**
500+ * struct ipu6_fw_isys_proxy_error_info_abi - ISYS proxy error
501+ * @error: error code if something went wrong
502+ * @error_details: depending on error code, it may contain additional error info
503+ */
504+struct ipu6_fw_isys_proxy_error_info_abi {
505+ u32 error;
506+ u32 error_details;
507+};
508+
509+struct ipu6_fw_isys_proxy_resp_info_abi {
510+ u32 request_id;
511+ struct ipu6_fw_isys_proxy_error_info_abi error_info;
512+};
513+
514+/**
515+ * struct ipu6_fw_proxy_write_queue_token - ISYS proxy write queue token
516+ * @request_id: update id for the specific proxy write request
517+ * @region_index: Region id for the proxy write request
518+ * @offset: Offset of the write request according to the base address
519+ * of the region
520+ * @value: Value that is requested to be written with the proxy write request
521+ */
522+struct ipu6_fw_proxy_write_queue_token {
523+ u32 request_id;
524+ u32 region_index;
525+ u32 offset;
526+ u32 value;
527+};
528+
529+/**
530+ * struct ipu6_fw_resp_queue_token - ISYS response queue token
531+ * @resp_info: response info
532+ */
533+struct ipu6_fw_resp_queue_token {
534+ struct ipu6_fw_isys_resp_info_abi resp_info;
535+};
536+
537+/**
538+ * struct ipu6_fw_send_queue_token - ISYS send queue token
539+ * @buf_handle: buffer handle
540+ * @payload: payload
541+ * @send_type: send_type
542+ * @stream_id: stream_id
543+ */
544+struct ipu6_fw_send_queue_token {
545+ u64 buf_handle;
546+ u32 payload;
547+ u16 send_type;
548+ u16 stream_id;
549+};
550+
551+/**
552+ * struct ipu6_fw_proxy_resp_queue_token - ISYS proxy response queue token
553+ * @proxy_resp_info: proxy response info
554+ */
555+struct ipu6_fw_proxy_resp_queue_token {
556+ struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info;
557+};
558+
559+/**
560+ * struct ipu6_fw_proxy_send_queue_token - SYS proxy send queue token
561+ * @request_id: request_id
562+ * @region_index: region_index
563+ * @offset: offset
564+ * @value: value
565+ */
566+struct ipu6_fw_proxy_send_queue_token {
567+ u32 request_id;
568+ u32 region_index;
569+ u32 offset;
570+ u32 value;
571+};
572+
573+void
574+ipu6_fw_isys_dump_stream_cfg(struct device *dev,
575+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg);
576+void
577+ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
578+ struct ipu6_fw_isys_frame_buff_set_abi *buf,
579+ unsigned int outputs);
580+int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams);
581+int ipu6_fw_isys_close(struct ipu6_isys *isys);
582+int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
583+ const unsigned int stream_handle, u16 send_type);
584+int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
585+ const unsigned int stream_handle,
586+ void *cpu_mapped_buf, dma_addr_t dma_mapped_buf,
587+ size_t size, u16 send_type);
588+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
589+ unsigned int req_id,
590+ unsigned int index,
591+ unsigned int offset, u32 value);
592+void ipu6_fw_isys_cleanup(struct ipu6_isys *isys);
593+struct ipu6_fw_isys_resp_info_abi *
594+ipu6_fw_isys_get_resp(void *context, unsigned int queue);
595+void ipu6_fw_isys_put_resp(void *context, unsigned int queue);
596+#endif
drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c+663−0
@@ -0,0 +1,663 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/atomic.h>
7+#include <linux/bitfield.h>
8+#include <linux/bits.h>
9+#include <linux/delay.h>
10+#include <linux/device.h>
11+#include <linux/err.h>
12+#include <linux/io.h>
13+#include <linux/minmax.h>
14+#include <linux/sprintf.h>
15+
16+#include <media/media-entity.h>
17+#include <media/v4l2-ctrls.h>
18+#include <media/v4l2-device.h>
19+#include <media/v4l2-event.h>
20+#include <media/v4l2-subdev.h>
21+
22+#include "ipu6-bus.h"
23+#include "ipu6-isys.h"
24+#include "ipu6-isys-csi2.h"
25+#include "ipu6-isys-subdev.h"
26+#include "ipu6-platform-isys-csi2-reg.h"
27+
28+static const u32 csi2_supported_codes[] = {
29+ MEDIA_BUS_FMT_RGB565_1X16,
30+ MEDIA_BUS_FMT_RGB888_1X24,
31+ MEDIA_BUS_FMT_UYVY8_1X16,
32+ MEDIA_BUS_FMT_YUYV8_1X16,
33+ MEDIA_BUS_FMT_SBGGR10_1X10,
34+ MEDIA_BUS_FMT_SGBRG10_1X10,
35+ MEDIA_BUS_FMT_SGRBG10_1X10,
36+ MEDIA_BUS_FMT_SRGGB10_1X10,
37+ MEDIA_BUS_FMT_SBGGR12_1X12,
38+ MEDIA_BUS_FMT_SGBRG12_1X12,
39+ MEDIA_BUS_FMT_SGRBG12_1X12,
40+ MEDIA_BUS_FMT_SRGGB12_1X12,
41+ MEDIA_BUS_FMT_SBGGR8_1X8,
42+ MEDIA_BUS_FMT_SGBRG8_1X8,
43+ MEDIA_BUS_FMT_SGRBG8_1X8,
44+ MEDIA_BUS_FMT_SRGGB8_1X8,
45+ MEDIA_BUS_FMT_META_8,
46+ MEDIA_BUS_FMT_META_10,
47+ MEDIA_BUS_FMT_META_12,
48+ MEDIA_BUS_FMT_META_16,
49+ MEDIA_BUS_FMT_META_24,
50+ 0
51+};
52+
53+/*
54+ * Strings corresponding to CSI-2 receiver errors are here.
55+ * Corresponding macros are defined in the header file.
56+ */
57+static const struct ipu6_csi2_error dphy_rx_errors[] = {
58+ { "Single packet header error corrected", true },
59+ { "Multiple packet header errors detected", true },
60+ { "Payload checksum (CRC) error", true },
61+ { "Transfer FIFO overflow", false },
62+ { "Reserved short packet data type detected", true },
63+ { "Reserved long packet data type detected", true },
64+ { "Incomplete long packet detected", false },
65+ { "Frame sync error", false },
66+ { "Line sync error", false },
67+ { "DPHY recoverable synchronization error", true },
68+ { "DPHY fatal error", false },
69+ { "DPHY elastic FIFO overflow", false },
70+ { "Inter-frame short packet discarded", true },
71+ { "Inter-frame long packet discarded", true },
72+ { "MIPI pktgen overflow", false },
73+ { "MIPI pktgen data loss", false },
74+ { "FIFO overflow", false },
75+ { "Lane deskew", false },
76+ { "SOT sync error", false },
77+ { "HSIDLE detected", false }
78+};
79+
80+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
81+{
82+ struct media_pad *src_pad;
83+ struct v4l2_subdev *ext_sd;
84+ struct device *dev;
85+
86+ if (!csi2)
87+ return -EINVAL;
88+
89+ dev = &csi2->isys->adev->auxdev.dev;
90+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
91+ if (IS_ERR(src_pad)) {
92+ dev_err(dev, "can't get source pad of %s (%ld)\n",
93+ csi2->asd.sd.name, PTR_ERR(src_pad));
94+ return PTR_ERR(src_pad);
95+ }
96+
97+ ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);
98+ if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))
99+ return -ENODEV;
100+
101+ return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0);
102+}
103+
104+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
105+ struct v4l2_event_subscription *sub)
106+{
107+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
108+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
109+ struct device *dev = &csi2->isys->adev->auxdev.dev;
110+
111+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
112+ sub->type, sub->id);
113+
114+ switch (sub->type) {
115+ case V4L2_EVENT_FRAME_SYNC:
116+ return v4l2_event_subscribe(fh, sub, 10, NULL);
117+ case V4L2_EVENT_CTRL:
118+ return v4l2_ctrl_subscribe_event(fh, sub);
119+ default:
120+ return -EINVAL;
121+ }
122+}
123+
124+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
125+ .subscribe_event = csi2_subscribe_event,
126+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
127+};
128+
129+/*
130+ * The input system CSI2+ receiver has several
131+ * parameters affecting the receiver timings. These depend
132+ * on the MIPI bus frequency F in Hz (sensor transmitter rate)
133+ * as follows:
134+ * register value = (A/1e9 + B * UI) / COUNT_ACC
135+ * where
136+ * UI = 1 / (2 * F) in seconds
137+ * COUNT_ACC = counter accuracy in seconds
138+ * COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.
139+ *
140+ * A and B are coefficients from the table below,
141+ * depending whether the register minimum or maximum value is
142+ * calculated.
143+ * Minimum Maximum
144+ * Clock lane A B A B
145+ * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0
146+ * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16
147+ * Data lanes
148+ * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4
149+ * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6
150+ * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4
151+ * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6
152+ * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4
153+ * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6
154+ * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4
155+ * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6
156+ *
157+ * We use the minimum values of both A and B.
158+ */
159+
160+#define DIV_SHIFT 8
161+#define CSI2_ACCINV 8
162+
163+static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)
164+{
165+ return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)
166+ / (s32)(link_freq >> DIV_SHIFT));
167+}
168+
169+static int
170+ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,
171+ struct ipu6_isys_csi2_timing *timing, s32 accinv)
172+{
173+ struct device *dev = &csi2->isys->adev->auxdev.dev;
174+ s64 link_freq;
175+
176+ link_freq = ipu6_isys_csi2_get_link_freq(csi2);
177+ if (link_freq < 0)
178+ return link_freq;
179+
180+ timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,
181+ CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,
182+ link_freq, accinv);
183+ timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,
184+ CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,
185+ link_freq, accinv);
186+ timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,
187+ CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,
188+ link_freq, accinv);
189+ timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,
190+ CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,
191+ link_freq, accinv);
192+
193+ dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",
194+ timing->ctermen, timing->csettle,
195+ timing->dtermen, timing->dsettle);
196+
197+ return 0;
198+}
199+
200+void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)
201+{
202+ u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
203+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
204+ struct ipu6_isys *isys = csi2->isys;
205+ u32 mask;
206+
207+ mask = isys->pdata->ipdata->csi2.irq_mask;
208+ writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
209+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
210+ csi2->receiver_errors |= irq & mask;
211+}
212+
213+void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
214+{
215+ struct device *dev = &csi2->isys->adev->auxdev.dev;
216+ const struct ipu6_csi2_error *errors;
217+ u32 status;
218+ u32 i;
219+
220+ /* register errors once more in case of interrupts are disabled */
221+ ipu6_isys_register_errors(csi2);
222+ status = csi2->receiver_errors;
223+ csi2->receiver_errors = 0;
224+ errors = dphy_rx_errors;
225+
226+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
227+ if (status & BIT(i))
228+ dev_err_ratelimited(dev, "csi2-%i error: %s\n",
229+ csi2->port, errors[i].error_string);
230+ }
231+}
232+
233+static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
234+ const struct ipu6_isys_csi2_timing *timing,
235+ unsigned int nlanes, int enable)
236+{
237+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
238+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
239+ struct ipu6_isys *isys = csi2->isys;
240+ struct device *dev = &isys->adev->auxdev.dev;
241+ struct ipu6_isys_csi2_config cfg;
242+ unsigned int nports;
243+ int ret = 0;
244+ u32 mask = 0;
245+ u32 i;
246+
247+ dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
248+ csi2->port, nlanes);
249+
250+ cfg.port = csi2->port;
251+ cfg.nlanes = nlanes;
252+
253+ mask = isys->pdata->ipdata->csi2.irq_mask;
254+ nports = isys->pdata->ipdata->csi2.nports;
255+
256+ if (!enable) {
257+ writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
258+ writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
259+
260+ writel(0,
261+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
262+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
263+ writel(mask,
264+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
265+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
266+ writel(0,
267+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
268+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
269+ writel(0xffffffff,
270+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
271+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
272+
273+ isys->phy_set_power(isys, &cfg, timing, false);
274+
275+ writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
276+ (isys->pdata->ipdata->csi2.fw_access_port_ofs,
277+ csi2->port));
278+ writel(0, isys->pdata->base +
279+ CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
280+
281+ return ret;
282+ }
283+
284+ /* reset port reset */
285+ writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
286+ usleep_range(100, 200);
287+ writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
288+
289+ /* enable port clock */
290+ for (i = 0; i < nports; i++) {
291+ writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));
292+ writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
293+ (isys->pdata->ipdata->csi2.fw_access_port_ofs, i));
294+ }
295+
296+ /* enable all error related irq */
297+ writel(mask,
298+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
299+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
300+ writel(mask,
301+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
302+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
303+ writel(mask,
304+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
305+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
306+ writel(mask,
307+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
308+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
309+ writel(mask,
310+ csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
311+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
312+
313+ /*
314+ * Using event from firmware instead of irq to handle CSI2 sync event
315+ * which can reduce system wakeups. If CSI2 sync irq enabled, we need
316+ * disable the firmware CSI2 sync event to avoid duplicate handling.
317+ */
318+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
319+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
320+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
321+ CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
322+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
323+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
324+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
325+ CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
326+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
327+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
328+
329+ /* configure to enable FE and PPI2CSI */
330+ writel(0, csi2->base + CSI_REG_CSI_FE_MODE);
331+ writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);
332+ writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,
333+ csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);
334+ writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),
335+ csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);
336+
337+ writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);
338+ writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);
339+
340+ ret = isys->phy_set_power(isys, &cfg, timing, true);
341+ if (ret)
342+ dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
343+ ret);
344+
345+ return ret;
346+}
347+
348+static int set_stream(struct v4l2_subdev *sd, int enable)
349+{
350+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
351+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
352+ struct device *dev = &csi2->isys->adev->auxdev.dev;
353+ struct ipu6_isys_csi2_timing timing = { };
354+ unsigned int nlanes;
355+ int ret;
356+
357+ dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off");
358+
359+ if (!enable) {
360+ csi2->stream_count--;
361+ if (csi2->stream_count)
362+ return 0;
363+
364+ ipu6_isys_csi2_set_stream(sd, &timing, 0, enable);
365+ return 0;
366+ }
367+
368+ if (csi2->stream_count) {
369+ csi2->stream_count++;
370+ return 0;
371+ }
372+
373+ nlanes = csi2->nlanes;
374+
375+ ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
376+ if (ret)
377+ return ret;
378+
379+ ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable);
380+ if (ret)
381+ return ret;
382+
383+ csi2->stream_count++;
384+
385+ return 0;
386+}
387+
388+static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
389+ struct v4l2_subdev_state *state,
390+ struct v4l2_subdev_selection *sel)
391+{
392+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
393+ struct device *dev = &asd->isys->adev->auxdev.dev;
394+ struct v4l2_mbus_framefmt *sink_ffmt;
395+ struct v4l2_mbus_framefmt *src_ffmt;
396+ struct v4l2_rect *crop;
397+
398+ if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
399+ return -EINVAL;
400+
401+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
402+ sel->pad,
403+ sel->stream);
404+ if (!sink_ffmt)
405+ return -EINVAL;
406+
407+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
408+ if (!src_ffmt)
409+ return -EINVAL;
410+
411+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
412+ if (!crop)
413+ return -EINVAL;
414+
415+ /* Only vertical cropping is supported */
416+ sel->r.left = 0;
417+ sel->r.width = sink_ffmt->width;
418+ /* Non-bayer formats can't be single line cropped */
419+ if (!ipu6_isys_is_bayer_format(sink_ffmt->code))
420+ sel->r.top &= ~1;
421+ sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,
422+ sink_ffmt->height - sel->r.top);
423+ *crop = sel->r;
424+
425+ /* update source pad format */
426+ src_ffmt->width = sel->r.width;
427+ src_ffmt->height = sel->r.height;
428+ if (ipu6_isys_is_bayer_format(sink_ffmt->code))
429+ src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,
430+ sel->r.left,
431+ sel->r.top);
432+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
433+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
434+ src_ffmt->code);
435+
436+ return 0;
437+}
438+
439+static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
440+ struct v4l2_subdev_state *state,
441+ struct v4l2_subdev_selection *sel)
442+{
443+ struct v4l2_mbus_framefmt *sink_ffmt;
444+ struct v4l2_rect *crop;
445+ int ret = 0;
446+
447+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
448+ return -EINVAL;
449+
450+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
451+ sel->pad,
452+ sel->stream);
453+ if (!sink_ffmt)
454+ return -EINVAL;
455+
456+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
457+ if (!crop)
458+ return -EINVAL;
459+
460+ switch (sel->target) {
461+ case V4L2_SEL_TGT_CROP_DEFAULT:
462+ case V4L2_SEL_TGT_CROP_BOUNDS:
463+ sel->r.left = 0;
464+ sel->r.top = 0;
465+ sel->r.width = sink_ffmt->width;
466+ sel->r.height = sink_ffmt->height;
467+ break;
468+ case V4L2_SEL_TGT_CROP:
469+ sel->r = *crop;
470+ break;
471+ default:
472+ ret = -EINVAL;
473+ }
474+
475+ return ret;
476+}
477+
478+static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
479+ .s_stream = set_stream,
480+};
481+
482+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
483+ .get_fmt = v4l2_subdev_get_fmt,
484+ .set_fmt = ipu6_isys_subdev_set_fmt,
485+ .get_selection = ipu6_isys_csi2_get_sel,
486+ .set_selection = ipu6_isys_csi2_set_sel,
487+ .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,
488+ .set_routing = ipu6_isys_subdev_set_routing,
489+};
490+
491+static const struct v4l2_subdev_ops csi2_sd_ops = {
492+ .core = &csi2_sd_core_ops,
493+ .video = &csi2_sd_video_ops,
494+ .pad = &csi2_sd_pad_ops,
495+};
496+
497+static const struct media_entity_operations csi2_entity_ops = {
498+ .link_validate = v4l2_subdev_link_validate,
499+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
500+};
501+
502+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)
503+{
504+ if (!csi2->isys)
505+ return;
506+
507+ v4l2_device_unregister_subdev(&csi2->asd.sd);
508+ v4l2_subdev_cleanup(&csi2->asd.sd);
509+ ipu6_isys_subdev_cleanup(&csi2->asd);
510+ csi2->isys = NULL;
511+}
512+
513+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
514+ struct ipu6_isys *isys,
515+ void __iomem *base, unsigned int index)
516+{
517+ struct device *dev = &isys->adev->auxdev.dev;
518+ int ret;
519+
520+ csi2->isys = isys;
521+ csi2->base = base;
522+ csi2->port = index;
523+
524+ csi2->asd.sd.entity.ops = &csi2_entity_ops;
525+ csi2->asd.isys = isys;
526+ ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
527+ NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
528+ if (ret)
529+ goto fail;
530+
531+ csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;
532+ csi2->asd.supported_codes = csi2_supported_codes;
533+ snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
534+ IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);
535+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
536+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
537+ if (ret) {
538+ dev_err(dev, "failed to init v4l2 subdev\n");
539+ goto fail;
540+ }
541+
542+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
543+ if (ret) {
544+ dev_err(dev, "failed to register v4l2 subdev\n");
545+ goto fail;
546+ }
547+
548+ return 0;
549+
550+fail:
551+ ipu6_isys_csi2_cleanup(csi2);
552+
553+ return ret;
554+}
555+
556+void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)
557+{
558+ struct video_device *vdev = stream->asd->sd.devnode;
559+ struct device *dev = &stream->isys->adev->auxdev.dev;
560+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
561+ struct v4l2_event ev = {
562+ .type = V4L2_EVENT_FRAME_SYNC,
563+ };
564+
565+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
566+ v4l2_event_queue(vdev, &ev);
567+
568+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
569+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
570+}
571+
572+void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
573+{
574+ struct device *dev = &stream->isys->adev->auxdev.dev;
575+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
576+ u32 frame_sequence = atomic_read(&stream->sequence);
577+
578+ dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
579+ csi2->port, frame_sequence);
580+}
581+
582+int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
583+ struct ipu6_isys_csi2 *csi2,
584+ struct media_entity *source_entity,
585+ struct v4l2_mbus_frame_desc_entry *entry)
586+{
587+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
588+ struct device *dev = &csi2->isys->adev->auxdev.dev;
589+ struct v4l2_mbus_frame_desc desc;
590+ struct v4l2_subdev *source;
591+ struct media_pad *pad;
592+ unsigned int i;
593+ int ret;
594+
595+ source = media_entity_to_v4l2_subdev(source_entity);
596+ if (!source)
597+ return -EPIPE;
598+
599+ pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);
600+ if (!pad)
601+ return -EPIPE;
602+
603+ ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
604+ if (ret)
605+ return ret;
606+
607+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
608+ dev_err(dev, "Unsupported frame descriptor type\n");
609+ return -EINVAL;
610+ }
611+
612+ for (i = 0; i < desc.num_entries; i++) {
613+ if (source_stream == desc.entry[i].stream) {
614+ desc_entry = &desc.entry[i];
615+ break;
616+ }
617+ }
618+
619+ if (!desc_entry) {
620+ dev_err(dev, "Failed to find stream %u from remote subdev\n",
621+ source_stream);
622+ return -EINVAL;
623+ }
624+
625+ if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {
626+ dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
627+ return -EINVAL;
628+ }
629+
630+ *entry = *desc_entry;
631+
632+ return 0;
633+}
634+
635+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status)
636+{
637+ struct ipu6_isys_stream *stream = av->stream;
638+ struct v4l2_subdev *sd = &stream->asd->sd;
639+ struct v4l2_subdev_state *state;
640+ struct media_pad *r_pad;
641+ unsigned int i;
642+ u32 r_stream;
643+
644+ r_pad = media_pad_remote_pad_first(&av->pad);
645+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
646+
647+ state = v4l2_subdev_lock_and_get_active_state(sd);
648+
649+ for (i = 0; i < state->stream_configs.num_configs; i++) {
650+ struct v4l2_subdev_stream_config *cfg =
651+ &state->stream_configs.configs[i];
652+
653+ if (cfg->pad == r_pad->index && r_stream == cfg->stream) {
654+ dev_dbg(&av->isys->adev->auxdev.dev,
655+ "%s: pad:%u, stream:%u, status:%u\n",
656+ sd->entity.name, r_pad->index, r_stream,
657+ status);
658+ cfg->enabled = status;
659+ }
660+ }
661+
662+ v4l2_subdev_unlock_state(state);
663+}
drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h+82−0
@@ -0,0 +1,82 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_ISYS_CSI2_H
5+#define IPU6_ISYS_CSI2_H
6+
7+#include <linux/container_of.h>
8+
9+#include "ipu6-isys-subdev.h"
10+#include "ipu6-isys-video.h"
11+
12+struct media_entity;
13+struct v4l2_mbus_frame_desc_entry;
14+
15+struct ipu6_isys_video;
16+struct ipu6_isys;
17+struct ipu6_isys_csi2_pdata;
18+struct ipu6_isys_stream;
19+
20+#define NR_OF_CSI2_VC 16
21+#define INVALID_VC_ID -1
22+#define NR_OF_CSI2_SINK_PADS 1
23+#define CSI2_PAD_SINK 0
24+#define NR_OF_CSI2_SRC_PADS 8
25+#define CSI2_PAD_SRC 1
26+#define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS)
27+
28+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0
29+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0
30+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95
31+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8
32+
33+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0
34+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0
35+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85
36+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2
37+
38+struct ipu6_isys_csi2 {
39+ struct ipu6_isys_subdev asd;
40+ struct ipu6_isys_csi2_pdata *pdata;
41+ struct ipu6_isys *isys;
42+ struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS];
43+
44+ void __iomem *base;
45+ u32 receiver_errors;
46+ unsigned int nlanes;
47+ unsigned int port;
48+ unsigned int stream_count;
49+};
50+
51+struct ipu6_isys_csi2_timing {
52+ u32 ctermen;
53+ u32 csettle;
54+ u32 dtermen;
55+ u32 dsettle;
56+};
57+
58+struct ipu6_csi2_error {
59+ const char *error_string;
60+ bool is_info_only;
61+};
62+
63+#define ipu6_isys_subdev_to_csi2(__sd) \
64+ container_of(__sd, struct ipu6_isys_csi2, asd)
65+
66+#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd)
67+
68+s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2);
69+int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys,
70+ void __iomem *base, unsigned int index);
71+void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2);
72+void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream);
73+void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream);
74+void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2);
75+void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2);
76+int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
77+ struct ipu6_isys_csi2 *csi2,
78+ struct media_entity *source_entity,
79+ struct v4l2_mbus_frame_desc_entry *entry);
80+void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status);
81+
82+#endif /* IPU6_ISYS_CSI2_H */
drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c+536−0
@@ -0,0 +1,536 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bitfield.h>
7+#include <linux/bits.h>
8+#include <linux/delay.h>
9+#include <linux/device.h>
10+#include <linux/iopoll.h>
11+#include <linux/math64.h>
12+
13+#include "ipu6-bus.h"
14+#include "ipu6-isys.h"
15+#include "ipu6-platform-isys-csi2-reg.h"
16+
17+#define IPU6_DWC_DPHY_BASE(i) (0x238038 + 0x34 * (i))
18+#define IPU6_DWC_DPHY_RSTZ 0x00
19+#define IPU6_DWC_DPHY_SHUTDOWNZ 0x04
20+#define IPU6_DWC_DPHY_HSFREQRANGE 0x08
21+#define IPU6_DWC_DPHY_CFGCLKFREQRANGE 0x0c
22+#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE 0x10
23+#define IPU6_DWC_DPHY_TEST_IFC_REQ 0x14
24+#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION 0x18
25+#define IPU6_DWC_DPHY_DFT_CTRL0 0x28
26+#define IPU6_DWC_DPHY_DFT_CTRL1 0x2c
27+#define IPU6_DWC_DPHY_DFT_CTRL2 0x30
28+
29+/*
30+ * test IFC request definition:
31+ * - req: 0 for read, 1 for write
32+ * - 12 bits address
33+ * - 8bits data (will ignore for read)
34+ * --24----16------4-----0
35+ * --|-data-|-addr-|-req-|
36+ */
37+#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
38+ FIELD_PREP(GENMASK(15, 4), addr) | \
39+ FIELD_PREP(GENMASK(1, 0), req))
40+
41+#define TEST_IFC_REQ_READ 0
42+#define TEST_IFC_REQ_WRITE 1
43+#define TEST_IFC_REQ_RESET 2
44+
45+#define TEST_IFC_ACCESS_MODE_FSM 0
46+#define TEST_IFC_ACCESS_MODE_IFC_CTL 1
47+
48+enum phy_fsm_state {
49+ PHY_FSM_STATE_POWERON = 0,
50+ PHY_FSM_STATE_BGPON = 1,
51+ PHY_FSM_STATE_CAL_TYPE = 2,
52+ PHY_FSM_STATE_BURNIN_CAL = 3,
53+ PHY_FSM_STATE_TERMCAL = 4,
54+ PHY_FSM_STATE_OFFSETCAL = 5,
55+ PHY_FSM_STATE_OFFSET_LANE = 6,
56+ PHY_FSM_STATE_IDLE = 7,
57+ PHY_FSM_STATE_ULP = 8,
58+ PHY_FSM_STATE_DDLTUNNING = 9,
59+ PHY_FSM_STATE_SKEW_BACKWARD = 10,
60+ PHY_FSM_STATE_INVALID,
61+};
62+
63+static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
64+ u32 data)
65+{
66+ struct device *dev = &isys->adev->auxdev.dev;
67+ void __iomem *isys_base = isys->pdata->base;
68+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
69+
70+ dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,
71+ data);
72+ writel(data, base + addr);
73+}
74+
75+static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
76+{
77+ struct device *dev = &isys->adev->auxdev.dev;
78+ void __iomem *isys_base = isys->pdata->base;
79+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
80+ u32 data;
81+
82+ data = readl(base + addr);
83+ dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,
84+ data);
85+
86+ return data;
87+}
88+
89+static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
90+ u32 data, u8 shift, u8 width)
91+{
92+ u32 temp;
93+ u32 mask;
94+
95+ mask = (1 << width) - 1;
96+ temp = dwc_dphy_read(isys, phy_id, addr);
97+ temp &= ~(mask << shift);
98+ temp |= (data & mask) << shift;
99+ dwc_dphy_write(isys, phy_id, addr, temp);
100+}
101+
102+static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
103+ u32 addr, u8 shift, u8 width)
104+{
105+ u32 val;
106+
107+ val = dwc_dphy_read(isys, phy_id, addr) >> shift;
108+ return val & ((1 << width) - 1);
109+}
110+
111+#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
112+static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
113+ u32 *val)
114+{
115+ struct device *dev = &isys->adev->auxdev.dev;
116+ void __iomem *isys_base = isys->pdata->base;
117+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
118+ void __iomem *reg;
119+ u32 completion;
120+ int ret;
121+
122+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
123+ IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
124+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
125+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
126+ 10, DWC_DPHY_TIMEOUT);
127+ if (ret) {
128+ dev_err(dev, "DWC ifc request read timeout\n");
129+ return ret;
130+ }
131+
132+ *val = completion >> 8 & 0xff;
133+ *val = FIELD_GET(GENMASK(15, 8), completion);
134+ dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
135+
136+ return 0;
137+}
138+
139+static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
140+ u32 data)
141+{
142+ struct device *dev = &isys->adev->auxdev.dev;
143+ void __iomem *isys_base = isys->pdata->base;
144+ void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
145+ void __iomem *reg;
146+ u32 completion;
147+ int ret;
148+
149+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
150+ IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
151+ completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
152+ reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
153+ ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
154+ 10, DWC_DPHY_TIMEOUT);
155+ if (ret)
156+ dev_err(dev, "DWC ifc request write timeout\n");
157+
158+ return ret;
159+}
160+
161+static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
162+ u32 addr, u32 data, u8 shift, u8 width)
163+{
164+ u32 temp, mask;
165+ int ret;
166+
167+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
168+ if (ret)
169+ return;
170+
171+ mask = (1 << width) - 1;
172+ temp &= ~(mask << shift);
173+ temp |= (data & mask) << shift;
174+ dwc_dphy_ifc_write(isys, phy_id, addr, temp);
175+}
176+
177+static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
178+ u8 shift, u8 width)
179+{
180+ int ret;
181+ u32 val;
182+
183+ ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
184+ if (ret)
185+ return 0;
186+
187+ return ((val >> shift) & ((1 << width) - 1));
188+}
189+
190+static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
191+{
192+ struct device *dev = &isys->adev->auxdev.dev;
193+ u32 fsm_state;
194+ int ret;
195+
196+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
197+ usleep_range(10, 20);
198+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
199+
200+ ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
201+ (fsm_state == PHY_FSM_STATE_IDLE ||
202+ fsm_state == PHY_FSM_STATE_ULP),
203+ 100, DWC_DPHY_TIMEOUT, false, isys,
204+ phy_id, 0x1e, 0, 4);
205+
206+ if (ret)
207+ dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
208+ fsm_state);
209+
210+ return ret;
211+}
212+
213+struct dwc_dphy_freq_range {
214+ u8 hsfreq;
215+ u16 min;
216+ u16 max;
217+ u16 default_mbps;
218+ u16 osc_freq_target;
219+};
220+
221+#define DPHY_FREQ_RANGE_NUM (63)
222+#define DPHY_FREQ_RANGE_INVALID_INDEX (0xff)
223+static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
224+ {0x00, 80, 97, 80, 335},
225+ {0x10, 80, 107, 90, 335},
226+ {0x20, 84, 118, 100, 335},
227+ {0x30, 93, 128, 110, 335},
228+ {0x01, 103, 139, 120, 335},
229+ {0x11, 112, 149, 130, 335},
230+ {0x21, 122, 160, 140, 335},
231+ {0x31, 131, 170, 150, 335},
232+ {0x02, 141, 181, 160, 335},
233+ {0x12, 150, 191, 170, 335},
234+ {0x22, 160, 202, 180, 335},
235+ {0x32, 169, 212, 190, 335},
236+ {0x03, 183, 228, 205, 335},
237+ {0x13, 198, 244, 220, 335},
238+ {0x23, 212, 259, 235, 335},
239+ {0x33, 226, 275, 250, 335},
240+ {0x04, 250, 301, 275, 335},
241+ {0x14, 274, 328, 300, 335},
242+ {0x25, 297, 354, 325, 335},
243+ {0x35, 321, 380, 350, 335},
244+ {0x05, 369, 433, 400, 335},
245+ {0x16, 416, 485, 450, 335},
246+ {0x26, 464, 538, 500, 335},
247+ {0x37, 511, 590, 550, 335},
248+ {0x07, 559, 643, 600, 335},
249+ {0x18, 606, 695, 650, 335},
250+ {0x28, 654, 748, 700, 335},
251+ {0x39, 701, 800, 750, 335},
252+ {0x09, 749, 853, 800, 335},
253+ {0x19, 796, 905, 850, 335},
254+ {0x29, 844, 958, 900, 335},
255+ {0x3a, 891, 1010, 950, 335},
256+ {0x0a, 939, 1063, 1000, 335},
257+ {0x1a, 986, 1115, 1050, 335},
258+ {0x2a, 1034, 1168, 1100, 335},
259+ {0x3b, 1081, 1220, 1150, 335},
260+ {0x0b, 1129, 1273, 1200, 335},
261+ {0x1b, 1176, 1325, 1250, 335},
262+ {0x2b, 1224, 1378, 1300, 335},
263+ {0x3c, 1271, 1430, 1350, 335},
264+ {0x0c, 1319, 1483, 1400, 335},
265+ {0x1c, 1366, 1535, 1450, 335},
266+ {0x2c, 1414, 1588, 1500, 335},
267+ {0x3d, 1461, 1640, 1550, 208},
268+ {0x0d, 1509, 1693, 1600, 214},
269+ {0x1d, 1556, 1745, 1650, 221},
270+ {0x2e, 1604, 1798, 1700, 228},
271+ {0x3e, 1651, 1850, 1750, 234},
272+ {0x0e, 1699, 1903, 1800, 241},
273+ {0x1e, 1746, 1955, 1850, 248},
274+ {0x2f, 1794, 2008, 1900, 255},
275+ {0x3f, 1841, 2060, 1950, 261},
276+ {0x0f, 1889, 2113, 2000, 268},
277+ {0x40, 1936, 2165, 2050, 275},
278+ {0x41, 1984, 2218, 2100, 281},
279+ {0x42, 2031, 2270, 2150, 288},
280+ {0x43, 2079, 2323, 2200, 294},
281+ {0x44, 2126, 2375, 2250, 302},
282+ {0x45, 2174, 2428, 2300, 308},
283+ {0x46, 2221, 2480, 2350, 315},
284+ {0x47, 2269, 2500, 2400, 321},
285+ {0x48, 2316, 2500, 2450, 328},
286+ {0x49, 2364, 2500, 2500, 335}
287+};
288+
289+static u16 get_hsfreq_by_mbps(u32 mbps)
290+{
291+ unsigned int i = DPHY_FREQ_RANGE_NUM;
292+
293+ while (i--) {
294+ if (freqranges[i].default_mbps == mbps ||
295+ (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
296+ return i;
297+ }
298+
299+ return DPHY_FREQ_RANGE_INVALID_INDEX;
300+}
301+
302+static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
303+ u32 phy_id, u32 mbps)
304+{
305+ struct ipu6_bus_device *adev = isys->adev;
306+ struct device *dev = &adev->auxdev.dev;
307+ struct ipu6_device *isp = adev->isp;
308+ u32 cfg_clk_freqrange;
309+ u32 osc_freq_target;
310+ u32 index;
311+
312+ dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
313+
314+ index = get_hsfreq_by_mbps(mbps);
315+ if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
316+ dev_err(dev, "link freq not found for mbps %u", mbps);
317+ return -EINVAL;
318+ }
319+
320+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
321+ freqranges[index].hsfreq, 0, 7);
322+
323+ /* Force termination Calibration */
324+ if (isys->phy_termcal_val) {
325+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
326+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
327+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
328+ isys->phy_termcal_val, 4, 4);
329+ }
330+
331+ /*
332+ * Enable override to configure the DDL target oscillation
333+ * frequency on bit 0 of register 0xe4
334+ */
335+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
336+ /*
337+ * configure registers 0xe2, 0xe3 with the
338+ * appropriate DDL target oscillation frequency
339+ * 0x1cc(460)
340+ */
341+ osc_freq_target = freqranges[index].osc_freq_target;
342+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
343+ osc_freq_target & 0xff, 0, 8);
344+ dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
345+ (osc_freq_target >> 8) & 0xf, 0, 4);
346+
347+ if (mbps < 1500) {
348+ /* deskew_polarity_rw, for < 1.5Gbps */
349+ dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
350+ }
351+
352+ /*
353+ * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
354+ * (38.4 - 17) * 4 = ~85 (0x55)
355+ */
356+ cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
357+ dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
358+ isp->buttress.ref_clk, cfg_clk_freqrange);
359+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
360+ cfg_clk_freqrange, 0, 8);
361+
362+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
363+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
364+
365+ return 0;
366+}
367+
368+static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
369+ u32 slave, u32 mbps)
370+{
371+ /* Config mastermacro */
372+ dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
373+ dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
374+
375+ /* Config master PHY clk lane to drive long channel clk */
376+ dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
377+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
378+
379+ /* Config both PHYs data lanes to get clk from long channel */
380+ dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
381+ dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
382+ dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
383+ dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
384+
385+ /* Config slave PHY clk lane to bypass long channel clk to DDR clk */
386+ dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
387+ dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
388+
389+ /* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
390+ dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
391+
392+ /* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
393+ dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
394+ dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
395+
396+ /* Turn off slave PHY LP-RX clk lane */
397+ dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
398+ dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
399+}
400+
401+#define PHY_E 4
402+static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
403+{
404+ struct device *dev = &isys->adev->auxdev.dev;
405+ u32 rescal_done;
406+ int ret;
407+
408+ ret = dwc_dphy_pwr_up(isys, phy_id);
409+ if (ret != 0) {
410+ dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
411+ return ret;
412+ }
413+
414+ /* reset forcerxmode */
415+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
416+ dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
417+
418+ dev_dbg(dev, "Dphy %u is ready!", phy_id);
419+
420+ if (phy_id != PHY_E || isys->phy_termcal_val)
421+ return 0;
422+
423+ usleep_range(100, 200);
424+ rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
425+ if (rescal_done) {
426+ isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
427+ 0x220, 2, 4);
428+ dev_dbg(dev, "termcal done with value = %u",
429+ isys->phy_termcal_val);
430+ }
431+
432+ return 0;
433+}
434+
435+static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
436+{
437+ dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
438+
439+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
440+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
441+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
442+ TEST_IFC_ACCESS_MODE_FSM);
443+ dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
444+ TEST_IFC_REQ_RESET);
445+}
446+
447+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
448+ struct ipu6_isys_csi2_config *cfg,
449+ const struct ipu6_isys_csi2_timing *timing,
450+ bool on)
451+{
452+ struct device *dev = &isys->adev->auxdev.dev;
453+ void __iomem *isys_base = isys->pdata->base;
454+ u32 phy_id, primary, secondary;
455+ u32 nlanes, port, mbps;
456+ s64 link_freq;
457+ int ret;
458+
459+ port = cfg->port;
460+
461+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
462+ dev_warn(dev, "invalid port ID %d\n", port);
463+ return -EINVAL;
464+ }
465+
466+ nlanes = cfg->nlanes;
467+ /* only port 0, 2 and 4 support 4 lanes */
468+ if (nlanes == 4 && port % 2) {
469+ dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
470+ nlanes);
471+ return -EINVAL;
472+ }
473+
474+ link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
475+ if (link_freq < 0) {
476+ dev_err(dev, "get link freq failed(%lld).\n", link_freq);
477+ return link_freq;
478+ }
479+
480+ mbps = div_u64(link_freq, 500000);
481+
482+ phy_id = port;
483+ primary = port & ~1;
484+ secondary = primary + 1;
485+ if (on) {
486+ if (nlanes == 4) {
487+ dev_dbg(dev, "config phy %u and %u in aggr mode\n",
488+ primary, secondary);
489+
490+ ipu6_isys_dwc_phy_reset(isys, primary);
491+ ipu6_isys_dwc_phy_reset(isys, secondary);
492+ ipu6_isys_dwc_phy_aggr_setup(isys, primary,
493+ secondary, mbps);
494+
495+ ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
496+ if (ret)
497+ return ret;
498+ ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
499+ if (ret)
500+ return ret;
501+
502+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
503+ if (ret)
504+ return ret;
505+
506+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
507+ return ret;
508+ }
509+
510+ dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
511+ phy_id, nlanes);
512+
513+ ipu6_isys_dwc_phy_reset(isys, phy_id);
514+ ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
515+ if (ret)
516+ return ret;
517+
518+ ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
519+ return ret;
520+ }
521+
522+ if (nlanes == 4) {
523+ dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
524+ primary, secondary, port);
525+ ipu6_isys_dwc_phy_reset(isys, secondary);
526+ ipu6_isys_dwc_phy_reset(isys, primary);
527+
528+ return 0;
529+ }
530+
531+ dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
532+
533+ ipu6_isys_dwc_phy_reset(isys, phy_id);
534+
535+ return 0;
536+}
drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c+242−0
@@ -0,0 +1,242 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bitfield.h>
7+#include <linux/bits.h>
8+#include <linux/device.h>
9+#include <linux/io.h>
10+
11+#include "ipu6-bus.h"
12+#include "ipu6-isys.h"
13+#include "ipu6-isys-csi2.h"
14+#include "ipu6-platform-isys-csi2-reg.h"
15+
16+/* only use BB0, BB2, BB4, and BB6 on PHY0 */
17+#define IPU6SE_ISYS_PHY_BB_NUM 4
18+#define IPU6SE_ISYS_PHY_0_BASE 0x10000
19+
20+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x))
21+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x))
22+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x))
23+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x))
24+
25+/*
26+ * use port_cfg to configure that which data lanes used
27+ * +---------+ +------+ +-----+
28+ * | port0 x4<-----| | | |
29+ * | | | port | | |
30+ * | port1 x2<-----| | | |
31+ * | | | <-| PHY |
32+ * | port2 x4<-----| | | |
33+ * | | |config| | |
34+ * | port3 x2<-----| | | |
35+ * +---------+ +------+ +-----+
36+ */
37+static const unsigned int csi2_port_cfg[][3] = {
38+ {0, 0, 0x1f}, /* no link */
39+ {4, 0, 0x10}, /* x4 + x4 config */
40+ {2, 0, 0x12}, /* x2 + x2 config */
41+ {1, 0, 0x13}, /* x1 + x1 config */
42+ {2, 1, 0x15}, /* x2x1 + x2x1 config */
43+ {1, 1, 0x16}, /* x1x1 + x1x1 config */
44+ {2, 2, 0x18}, /* x2x2 + x2x2 config */
45+ {1, 2, 0x19} /* x1x2 + x1x2 config */
46+};
47+
48+/* port, nlanes, bbindex, portcfg */
49+static const unsigned int phy_port_cfg[][4] = {
50+ /* sip0 */
51+ {0, 1, 0, 0x15},
52+ {0, 2, 0, 0x15},
53+ {0, 4, 0, 0x15},
54+ {0, 4, 2, 0x22},
55+ /* sip1 */
56+ {2, 1, 4, 0x15},
57+ {2, 2, 4, 0x15},
58+ {2, 4, 4, 0x15},
59+ {2, 4, 6, 0x22}
60+};
61+
62+static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys,
63+ unsigned int port,
64+ unsigned int nlanes)
65+{
66+ struct device *dev = &isys->adev->auxdev.dev;
67+ void __iomem *base = isys->adev->isp->base;
68+ unsigned int bbnum;
69+ u32 val, reg, i;
70+
71+ dev_dbg(dev, "port %u with %u lanes", port, nlanes);
72+
73+ /* only support <1.5Gbps */
74+ for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) {
75+ /* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */
76+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
77+ val = readl(base + reg);
78+ val |= FIELD_PREP(GENMASK(6, 1), 13);
79+ writel(val, base + reg);
80+
81+ /* cphy_rx_control1.en_crc1 = 1 */
82+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i);
83+ val = readl(base + reg);
84+ val |= BIT(31);
85+ writel(val, base + reg);
86+
87+ /* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */
88+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i);
89+ val = readl(base + reg);
90+ val |= BIT(25) | BIT(26);
91+ writel(val, base + reg);
92+
93+ /* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */
94+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
95+ val = readl(base + reg);
96+ val |= BIT(0);
97+ writel(val, base + reg);
98+ }
99+
100+ /* Front end config, use minimal channel loss */
101+ for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) {
102+ if (phy_port_cfg[i][0] == port &&
103+ phy_port_cfg[i][1] == nlanes) {
104+ bbnum = phy_port_cfg[i][2] / 2;
105+ reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum);
106+ val = readl(base + reg);
107+ val |= phy_port_cfg[i][3];
108+ writel(val, base + reg);
109+ }
110+ }
111+}
112+
113+static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys)
114+{
115+ void __iomem *base = isys->adev->isp->base;
116+ u32 val, reg;
117+
118+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL;
119+ val = readl(base + reg);
120+ val |= BIT(0);
121+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL);
122+
123+ reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL;
124+ val = readl(base + reg);
125+ val |= BIT(0);
126+ writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL);
127+
128+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL;
129+ val = readl(base + reg);
130+ val |= BIT(0);
131+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL);
132+
133+ reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL;
134+ val = readl(base + reg);
135+ val |= BIT(0);
136+ writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL);
137+}
138+
139+static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys,
140+ unsigned int port, unsigned int nlanes)
141+{
142+ struct device *dev = &isys->adev->auxdev.dev;
143+ unsigned int sip = port / 2;
144+ unsigned int index;
145+
146+ switch (nlanes) {
147+ case 1:
148+ index = 5;
149+ break;
150+ case 2:
151+ index = 6;
152+ break;
153+ case 4:
154+ index = 1;
155+ break;
156+ default:
157+ dev_err(dev, "lanes nr %u is unsupported\n", nlanes);
158+ return -EINVAL;
159+ }
160+
161+ dev_dbg(dev, "port config for port %u with %u lanes\n", port, nlanes);
162+
163+ writel(csi2_port_cfg[index][2],
164+ isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip));
165+
166+ return 0;
167+}
168+
169+static void
170+ipu6_isys_csi2_set_timing(struct ipu6_isys *isys,
171+ const struct ipu6_isys_csi2_timing *timing,
172+ unsigned int port, unsigned int nlanes)
173+{
174+ struct device *dev = &isys->adev->auxdev.dev;
175+ void __iomem *reg;
176+ u32 port_base;
177+ u32 i;
178+
179+ port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) :
180+ CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port);
181+
182+ dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes);
183+
184+ reg = isys->pdata->base + port_base;
185+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE;
186+
187+ writel(timing->ctermen, reg);
188+
189+ reg = isys->pdata->base + port_base;
190+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE;
191+ writel(timing->csettle, reg);
192+
193+ for (i = 0; i < nlanes; i++) {
194+ reg = isys->pdata->base + port_base;
195+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i);
196+ writel(timing->dtermen, reg);
197+
198+ reg = isys->pdata->base + port_base;
199+ reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i);
200+ writel(timing->dsettle, reg);
201+ }
202+}
203+
204+#define DPHY_TIMER_INCR 0x28
205+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
206+ struct ipu6_isys_csi2_config *cfg,
207+ const struct ipu6_isys_csi2_timing *timing,
208+ bool on)
209+{
210+ struct device *dev = &isys->adev->auxdev.dev;
211+ void __iomem *isys_base = isys->pdata->base;
212+ int ret = 0;
213+ u32 nlanes;
214+ u32 port;
215+
216+ if (!on)
217+ return 0;
218+
219+ port = cfg->port;
220+ nlanes = cfg->nlanes;
221+
222+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
223+ dev_warn(dev, "invalid port ID %d\n", port);
224+ return -EINVAL;
225+ }
226+
227+ ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes);
228+
229+ writel(DPHY_TIMER_INCR,
230+ isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR);
231+
232+ /* set port cfg and rx timing */
233+ ipu6_isys_csi2_set_timing(isys, timing, port, nlanes);
234+
235+ ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes);
236+ if (ret)
237+ return ret;
238+
239+ ipu6_isys_csi2_rx_control(isys);
240+
241+ return 0;
242+}
drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c+720−0
@@ -0,0 +1,720 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bits.h>
7+#include <linux/container_of.h>
8+#include <linux/device.h>
9+#include <linux/iopoll.h>
10+#include <linux/list.h>
11+#include <linux/refcount.h>
12+#include <linux/time64.h>
13+
14+#include <media/v4l2-async.h>
15+
16+#include "ipu6.h"
17+#include "ipu6-bus.h"
18+#include "ipu6-isys.h"
19+#include "ipu6-isys-csi2.h"
20+#include "ipu6-platform-isys-csi2-reg.h"
21+
22+#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8)
23+#define CSI_REG_HUB_GPREG_PHY_CTL_RESET BIT(4)
24+#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN BIT(0)
25+#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8)
26+#define CSI_REG_HUB_GPREG_PHY_POWER_ACK BIT(0)
27+#define CSI_REG_HUB_GPREG_PHY_READY BIT(4)
28+
29+#define MCD_PHY_POWER_STATUS_TIMEOUT (200 * USEC_PER_MSEC)
30+
31+/*
32+ * bridge to phy in buttress reg map, each phy has 16 kbytes
33+ * only 2 phys for TGL U and Y
34+ */
35+#define IPU6_ISYS_MCD_PHY_BASE(i) (0x10000 + (i) * 0x4000)
36+
37+/*
38+ * There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL.
39+ * Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes.
40+ * CSI port 1, 3 (5, 7) can support max 2 data lanes.
41+ * CSI port 0, 2 (4, 6) can support max 4 data lanes.
42+ * PHY configurations are PPI based instead of port.
43+ * Left:
44+ * +---------+---------+---------+---------+--------+---------+----------+
45+ * | | | | | | | |
46+ * | PPI | PPI5 | PPI4 | PPI3 | PPI2 | PPI1 | PPI0 |
47+ * +---------+---------+---------+---------+--------+---------+----------+
48+ * | | | | | | | |
49+ * | x4 | unused | D3 | D2 | C0 | D0 | D1 |
50+ * |---------+---------+---------+---------+--------+---------+----------+
51+ * | | | | | | | |
52+ * | x2x2 | C1 | D0 | D1 | C0 | D0 | D1 |
53+ * ----------+---------+---------+---------+--------+---------+----------+
54+ * | | | | | | | |
55+ * | x2x1 | C1 | D0 | unused | C0 | D0 | D1 |
56+ * +---------+---------+---------+---------+--------+---------+----------+
57+ * | | | | | | | |
58+ * | x1x1 | C1 | D0 | unused | C0 | D0 | unused |
59+ * +---------+---------+---------+---------+--------+---------+----------+
60+ * | | | | | | | |
61+ * | x1x2 | C1 | D0 | D1 | C0 | D0 | unused |
62+ * +---------+---------+---------+---------+--------+---------+----------+
63+ *
64+ * Right:
65+ * +---------+---------+---------+---------+--------+---------+----------+
66+ * | | | | | | | |
67+ * | PPI | PPI6 | PPI7 | PPI8 | PPI9 | PPI10 | PPI11 |
68+ * +---------+---------+---------+---------+--------+---------+----------+
69+ * | | | | | | | |
70+ * | x4 | D1 | D0 | C2 | D2 | D3 | unused |
71+ * |---------+---------+---------+---------+--------+---------+----------+
72+ * | | | | | | | |
73+ * | x2x2 | D1 | D0 | C2 | D1 | D0 | C3 |
74+ * ----------+---------+---------+---------+--------+---------+----------+
75+ * | | | | | | | |
76+ * | x2x1 | D1 | D0 | C2 | unused | D0 | C3 |
77+ * +---------+---------+---------+---------+--------+---------+----------+
78+ * | | | | | | | |
79+ * | x1x1 | unused | D0 | C2 | unused | D0 | C3 |
80+ * +---------+---------+---------+---------+--------+---------+----------+
81+ * | | | | | | | |
82+ * | x1x2 | unused | D0 | C2 | D1 | D0 | C3 |
83+ * +---------+---------+---------+---------+--------+---------+----------+
84+ *
85+ * ppi mapping per phy :
86+ *
87+ * x4 + x4:
88+ * Left : port0 - PPI range {0, 1, 2, 3, 4}
89+ * Right: port2 - PPI range {6, 7, 8, 9, 10}
90+ *
91+ * x4 + x2x2:
92+ * Left: port0 - PPI range {0, 1, 2, 3, 4}
93+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
94+ *
95+ * x2x2 + x4:
96+ * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
97+ * Right: port2 - PPI range {6, 7, 8, 9, 10}
98+ *
99+ * x2x2 + x2x2:
100+ * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
101+ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
102+ */
103+
104+struct phy_reg {
105+ u32 reg;
106+ u32 val;
107+};
108+
109+static const struct phy_reg common_init_regs[] = {
110+ /* for TGL-U, use 0x80000000 */
111+ {0x00000040, 0x80000000},
112+ {0x00000044, 0x00a80880},
113+ {0x00000044, 0x00b80880},
114+ {0x00000010, 0x0000078c},
115+ {0x00000344, 0x2f4401e2},
116+ {0x00000544, 0x924401e2},
117+ {0x00000744, 0x594401e2},
118+ {0x00000944, 0x624401e2},
119+ {0x00000b44, 0xfc4401e2},
120+ {0x00000d44, 0xc54401e2},
121+ {0x00000f44, 0x034401e2},
122+ {0x00001144, 0x8f4401e2},
123+ {0x00001344, 0x754401e2},
124+ {0x00001544, 0xe94401e2},
125+ {0x00001744, 0xcb4401e2},
126+ {0x00001944, 0xfa4401e2}
127+};
128+
129+static const struct phy_reg x1_port0_config_regs[] = {
130+ {0x00000694, 0xc80060fa},
131+ {0x00000680, 0x3d4f78ea},
132+ {0x00000690, 0x10a0140b},
133+ {0x000006a8, 0xdf04010a},
134+ {0x00000700, 0x57050060},
135+ {0x00000710, 0x0030001c},
136+ {0x00000738, 0x5f004444},
137+ {0x0000073c, 0x78464204},
138+ {0x00000748, 0x7821f940},
139+ {0x0000074c, 0xb2000433},
140+ {0x00000494, 0xfe6030fa},
141+ {0x00000480, 0x29ef5ed0},
142+ {0x00000490, 0x10a0540b},
143+ {0x000004a8, 0x7a01010a},
144+ {0x00000500, 0xef053460},
145+ {0x00000510, 0xe030101c},
146+ {0x00000538, 0xdf808444},
147+ {0x0000053c, 0xc8422204},
148+ {0x00000540, 0x0180088c},
149+ {0x00000574, 0x00000000},
150+ {0x00000000, 0x00000000}
151+};
152+
153+static const struct phy_reg x1_port1_config_regs[] = {
154+ {0x00000c94, 0xc80060fa},
155+ {0x00000c80, 0xcf47abea},
156+ {0x00000c90, 0x10a0840b},
157+ {0x00000ca8, 0xdf04010a},
158+ {0x00000d00, 0x57050060},
159+ {0x00000d10, 0x0030001c},
160+ {0x00000d38, 0x5f004444},
161+ {0x00000d3c, 0x78464204},
162+ {0x00000d48, 0x7821f940},
163+ {0x00000d4c, 0xb2000433},
164+ {0x00000a94, 0xc91030fa},
165+ {0x00000a80, 0x5a166ed0},
166+ {0x00000a90, 0x10a0540b},
167+ {0x00000aa8, 0x5d060100},
168+ {0x00000b00, 0xef053460},
169+ {0x00000b10, 0xa030101c},
170+ {0x00000b38, 0xdf808444},
171+ {0x00000b3c, 0xc8422204},
172+ {0x00000b40, 0x0180088c},
173+ {0x00000b74, 0x00000000},
174+ {0x00000000, 0x00000000}
175+};
176+
177+static const struct phy_reg x1_port2_config_regs[] = {
178+ {0x00001294, 0x28f000fa},
179+ {0x00001280, 0x08130cea},
180+ {0x00001290, 0x10a0140b},
181+ {0x000012a8, 0xd704010a},
182+ {0x00001300, 0x8d050060},
183+ {0x00001310, 0x0030001c},
184+ {0x00001338, 0xdf008444},
185+ {0x0000133c, 0x78422204},
186+ {0x00001348, 0x7821f940},
187+ {0x0000134c, 0x5a000433},
188+ {0x00001094, 0x2d20b0fa},
189+ {0x00001080, 0xade75dd0},
190+ {0x00001090, 0x10a0540b},
191+ {0x000010a8, 0xb101010a},
192+ {0x00001100, 0x33053460},
193+ {0x00001110, 0x0030101c},
194+ {0x00001138, 0xdf808444},
195+ {0x0000113c, 0xc8422204},
196+ {0x00001140, 0x8180088c},
197+ {0x00001174, 0x00000000},
198+ {0x00000000, 0x00000000}
199+};
200+
201+static const struct phy_reg x1_port3_config_regs[] = {
202+ {0x00001894, 0xc80060fa},
203+ {0x00001880, 0x0f90fd6a},
204+ {0x00001890, 0x10a0840b},
205+ {0x000018a8, 0xdf04010a},
206+ {0x00001900, 0x57050060},
207+ {0x00001910, 0x0030001c},
208+ {0x00001938, 0x5f004444},
209+ {0x0000193c, 0x78464204},
210+ {0x00001948, 0x7821f940},
211+ {0x0000194c, 0xb2000433},
212+ {0x00001694, 0x3050d0fa},
213+ {0x00001680, 0x0ef6d050},
214+ {0x00001690, 0x10a0540b},
215+ {0x000016a8, 0xe301010a},
216+ {0x00001700, 0x69053460},
217+ {0x00001710, 0xa030101c},
218+ {0x00001738, 0xdf808444},
219+ {0x0000173c, 0xc8422204},
220+ {0x00001740, 0x0180088c},
221+ {0x00001774, 0x00000000},
222+ {0x00000000, 0x00000000}
223+};
224+
225+static const struct phy_reg x2_port0_config_regs[] = {
226+ {0x00000694, 0xc80060fa},
227+ {0x00000680, 0x3d4f78ea},
228+ {0x00000690, 0x10a0140b},
229+ {0x000006a8, 0xdf04010a},
230+ {0x00000700, 0x57050060},
231+ {0x00000710, 0x0030001c},
232+ {0x00000738, 0x5f004444},
233+ {0x0000073c, 0x78464204},
234+ {0x00000748, 0x7821f940},
235+ {0x0000074c, 0xb2000433},
236+ {0x00000494, 0xc80060fa},
237+ {0x00000480, 0x29ef5ed8},
238+ {0x00000490, 0x10a0540b},
239+ {0x000004a8, 0x7a01010a},
240+ {0x00000500, 0xef053460},
241+ {0x00000510, 0xe030101c},
242+ {0x00000538, 0xdf808444},
243+ {0x0000053c, 0xc8422204},
244+ {0x00000540, 0x0180088c},
245+ {0x00000574, 0x00000000},
246+ {0x00000294, 0xc80060fa},
247+ {0x00000280, 0xcb45b950},
248+ {0x00000290, 0x10a0540b},
249+ {0x000002a8, 0x8c01010a},
250+ {0x00000300, 0xef053460},
251+ {0x00000310, 0x8030101c},
252+ {0x00000338, 0x41808444},
253+ {0x0000033c, 0x32422204},
254+ {0x00000340, 0x0180088c},
255+ {0x00000374, 0x00000000},
256+ {0x00000000, 0x00000000}
257+};
258+
259+static const struct phy_reg x2_port1_config_regs[] = {
260+ {0x00000c94, 0xc80060fa},
261+ {0x00000c80, 0xcf47abea},
262+ {0x00000c90, 0x10a0840b},
263+ {0x00000ca8, 0xdf04010a},
264+ {0x00000d00, 0x57050060},
265+ {0x00000d10, 0x0030001c},
266+ {0x00000d38, 0x5f004444},
267+ {0x00000d3c, 0x78464204},
268+ {0x00000d48, 0x7821f940},
269+ {0x00000d4c, 0xb2000433},
270+ {0x00000a94, 0xc80060fa},
271+ {0x00000a80, 0x5a166ed8},
272+ {0x00000a90, 0x10a0540b},
273+ {0x00000aa8, 0x7a01010a},
274+ {0x00000b00, 0xef053460},
275+ {0x00000b10, 0xa030101c},
276+ {0x00000b38, 0xdf808444},
277+ {0x00000b3c, 0xc8422204},
278+ {0x00000b40, 0x0180088c},
279+ {0x00000b74, 0x00000000},
280+ {0x00000894, 0xc80060fa},
281+ {0x00000880, 0x4d4f21d0},
282+ {0x00000890, 0x10a0540b},
283+ {0x000008a8, 0x5601010a},
284+ {0x00000900, 0xef053460},
285+ {0x00000910, 0x8030101c},
286+ {0x00000938, 0xdf808444},
287+ {0x0000093c, 0xc8422204},
288+ {0x00000940, 0x0180088c},
289+ {0x00000974, 0x00000000},
290+ {0x00000000, 0x00000000}
291+};
292+
293+static const struct phy_reg x2_port2_config_regs[] = {
294+ {0x00001294, 0xc80060fa},
295+ {0x00001280, 0x08130cea},
296+ {0x00001290, 0x10a0140b},
297+ {0x000012a8, 0xd704010a},
298+ {0x00001300, 0x8d050060},
299+ {0x00001310, 0x0030001c},
300+ {0x00001338, 0xdf008444},
301+ {0x0000133c, 0x78422204},
302+ {0x00001348, 0x7821f940},
303+ {0x0000134c, 0x5a000433},
304+ {0x00001094, 0xc80060fa},
305+ {0x00001080, 0xade75dd8},
306+ {0x00001090, 0x10a0540b},
307+ {0x000010a8, 0xb101010a},
308+ {0x00001100, 0x33053460},
309+ {0x00001110, 0x0030101c},
310+ {0x00001138, 0xdf808444},
311+ {0x0000113c, 0xc8422204},
312+ {0x00001140, 0x8180088c},
313+ {0x00001174, 0x00000000},
314+ {0x00000e94, 0xc80060fa},
315+ {0x00000e80, 0x0fbf16d0},
316+ {0x00000e90, 0x10a0540b},
317+ {0x00000ea8, 0x7a01010a},
318+ {0x00000f00, 0xf5053460},
319+ {0x00000f10, 0xc030101c},
320+ {0x00000f38, 0xdf808444},
321+ {0x00000f3c, 0xc8422204},
322+ {0x00000f40, 0x8180088c},
323+ {0x00000000, 0x00000000}
324+};
325+
326+static const struct phy_reg x2_port3_config_regs[] = {
327+ {0x00001894, 0xc80060fa},
328+ {0x00001880, 0x0f90fd6a},
329+ {0x00001890, 0x10a0840b},
330+ {0x000018a8, 0xdf04010a},
331+ {0x00001900, 0x57050060},
332+ {0x00001910, 0x0030001c},
333+ {0x00001938, 0x5f004444},
334+ {0x0000193c, 0x78464204},
335+ {0x00001948, 0x7821f940},
336+ {0x0000194c, 0xb2000433},
337+ {0x00001694, 0xc80060fa},
338+ {0x00001680, 0x0ef6d058},
339+ {0x00001690, 0x10a0540b},
340+ {0x000016a8, 0x7a01010a},
341+ {0x00001700, 0x69053460},
342+ {0x00001710, 0xa030101c},
343+ {0x00001738, 0xdf808444},
344+ {0x0000173c, 0xc8422204},
345+ {0x00001740, 0x0180088c},
346+ {0x00001774, 0x00000000},
347+ {0x00001494, 0xc80060fa},
348+ {0x00001480, 0xf9d34bd0},
349+ {0x00001490, 0x10a0540b},
350+ {0x000014a8, 0x7a01010a},
351+ {0x00001500, 0x1b053460},
352+ {0x00001510, 0x0030101c},
353+ {0x00001538, 0xdf808444},
354+ {0x0000153c, 0xc8422204},
355+ {0x00001540, 0x8180088c},
356+ {0x00001574, 0x00000000},
357+ {0x00000000, 0x00000000}
358+};
359+
360+static const struct phy_reg x4_port0_config_regs[] = {
361+ {0x00000694, 0xc80060fa},
362+ {0x00000680, 0x3d4f78fa},
363+ {0x00000690, 0x10a0140b},
364+ {0x000006a8, 0xdf04010a},
365+ {0x00000700, 0x57050060},
366+ {0x00000710, 0x0030001c},
367+ {0x00000738, 0x5f004444},
368+ {0x0000073c, 0x78464204},
369+ {0x00000748, 0x7821f940},
370+ {0x0000074c, 0xb2000433},
371+ {0x00000494, 0xfe6030fa},
372+ {0x00000480, 0x29ef5ed8},
373+ {0x00000490, 0x10a0540b},
374+ {0x000004a8, 0x7a01010a},
375+ {0x00000500, 0xef053460},
376+ {0x00000510, 0xe030101c},
377+ {0x00000538, 0xdf808444},
378+ {0x0000053c, 0xc8422204},
379+ {0x00000540, 0x0180088c},
380+ {0x00000574, 0x00000004},
381+ {0x00000294, 0x23e030fa},
382+ {0x00000280, 0xcb45b950},
383+ {0x00000290, 0x10a0540b},
384+ {0x000002a8, 0x8c01010a},
385+ {0x00000300, 0xef053460},
386+ {0x00000310, 0x8030101c},
387+ {0x00000338, 0x41808444},
388+ {0x0000033c, 0x32422204},
389+ {0x00000340, 0x0180088c},
390+ {0x00000374, 0x00000004},
391+ {0x00000894, 0x5620b0fa},
392+ {0x00000880, 0x4d4f21dc},
393+ {0x00000890, 0x10a0540b},
394+ {0x000008a8, 0x5601010a},
395+ {0x00000900, 0xef053460},
396+ {0x00000910, 0x8030101c},
397+ {0x00000938, 0xdf808444},
398+ {0x0000093c, 0xc8422204},
399+ {0x00000940, 0x0180088c},
400+ {0x00000974, 0x00000004},
401+ {0x00000a94, 0xc91030fa},
402+ {0x00000a80, 0x5a166ecc},
403+ {0x00000a90, 0x10a0540b},
404+ {0x00000aa8, 0x5d01010a},
405+ {0x00000b00, 0xef053460},
406+ {0x00000b10, 0xa030101c},
407+ {0x00000b38, 0xdf808444},
408+ {0x00000b3c, 0xc8422204},
409+ {0x00000b40, 0x0180088c},
410+ {0x00000b74, 0x00000004},
411+ {0x00000000, 0x00000000}
412+};
413+
414+static const struct phy_reg x4_port1_config_regs[] = {
415+ {0x00000000, 0x00000000}
416+};
417+
418+static const struct phy_reg x4_port2_config_regs[] = {
419+ {0x00001294, 0x28f000fa},
420+ {0x00001280, 0x08130cfa},
421+ {0x00001290, 0x10c0140b},
422+ {0x000012a8, 0xd704010a},
423+ {0x00001300, 0x8d050060},
424+ {0x00001310, 0x0030001c},
425+ {0x00001338, 0xdf008444},
426+ {0x0000133c, 0x78422204},
427+ {0x00001348, 0x7821f940},
428+ {0x0000134c, 0x5a000433},
429+ {0x00001094, 0x2d20b0fa},
430+ {0x00001080, 0xade75dd8},
431+ {0x00001090, 0x10a0540b},
432+ {0x000010a8, 0xb101010a},
433+ {0x00001100, 0x33053460},
434+ {0x00001110, 0x0030101c},
435+ {0x00001138, 0xdf808444},
436+ {0x0000113c, 0xc8422204},
437+ {0x00001140, 0x8180088c},
438+ {0x00001174, 0x00000004},
439+ {0x00000e94, 0xd308d0fa},
440+ {0x00000e80, 0x0fbf16d0},
441+ {0x00000e90, 0x10a0540b},
442+ {0x00000ea8, 0x2c01010a},
443+ {0x00000f00, 0xf5053460},
444+ {0x00000f10, 0xc030101c},
445+ {0x00000f38, 0xdf808444},
446+ {0x00000f3c, 0xc8422204},
447+ {0x00000f40, 0x8180088c},
448+ {0x00000f74, 0x00000004},
449+ {0x00001494, 0x136850fa},
450+ {0x00001480, 0xf9d34bdc},
451+ {0x00001490, 0x10a0540b},
452+ {0x000014a8, 0x5a01010a},
453+ {0x00001500, 0x1b053460},
454+ {0x00001510, 0x0030101c},
455+ {0x00001538, 0xdf808444},
456+ {0x0000153c, 0xc8422204},
457+ {0x00001540, 0x8180088c},
458+ {0x00001574, 0x00000004},
459+ {0x00001694, 0x3050d0fa},
460+ {0x00001680, 0x0ef6d04c},
461+ {0x00001690, 0x10a0540b},
462+ {0x000016a8, 0xe301010a},
463+ {0x00001700, 0x69053460},
464+ {0x00001710, 0xa030101c},
465+ {0x00001738, 0xdf808444},
466+ {0x0000173c, 0xc8422204},
467+ {0x00001740, 0x0180088c},
468+ {0x00001774, 0x00000004},
469+ {0x00000000, 0x00000000}
470+};
471+
472+static const struct phy_reg x4_port3_config_regs[] = {
473+ {0x00000000, 0x00000000}
474+};
475+
476+static const struct phy_reg *x1_config_regs[4] = {
477+ x1_port0_config_regs,
478+ x1_port1_config_regs,
479+ x1_port2_config_regs,
480+ x1_port3_config_regs
481+};
482+
483+static const struct phy_reg *x2_config_regs[4] = {
484+ x2_port0_config_regs,
485+ x2_port1_config_regs,
486+ x2_port2_config_regs,
487+ x2_port3_config_regs
488+};
489+
490+static const struct phy_reg *x4_config_regs[4] = {
491+ x4_port0_config_regs,
492+ x4_port1_config_regs,
493+ x4_port2_config_regs,
494+ x4_port3_config_regs
495+};
496+
497+static const struct phy_reg **config_regs[3] = {
498+ x1_config_regs,
499+ x2_config_regs,
500+ x4_config_regs
501+};
502+
503+static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id)
504+{
505+ struct device *dev = &isys->adev->auxdev.dev;
506+ void __iomem *isys_base = isys->pdata->base;
507+ u32 val;
508+ int ret;
509+
510+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
511+ val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN;
512+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
513+
514+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
515+ val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK,
516+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
517+ if (ret)
518+ dev_err(dev, "PHY%d powerup ack timeout", id);
519+
520+ return ret;
521+}
522+
523+static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id)
524+{
525+ struct device *dev = &isys->adev->auxdev.dev;
526+ void __iomem *isys_base = isys->pdata->base;
527+ u32 val;
528+ int ret;
529+
530+ writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
531+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
532+ val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK),
533+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
534+ if (ret)
535+ dev_err(dev, "PHY%d powerdown ack timeout", id);
536+
537+ return ret;
538+}
539+
540+static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert)
541+{
542+ void __iomem *isys_base = isys->pdata->base;
543+ u32 val;
544+
545+ val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
546+ if (assert)
547+ val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET;
548+ else
549+ val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET);
550+
551+ writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
552+}
553+
554+static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id)
555+{
556+ struct device *dev = &isys->adev->auxdev.dev;
557+ void __iomem *isys_base = isys->pdata->base;
558+ u32 val;
559+ int ret;
560+
561+ ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
562+ val, val & CSI_REG_HUB_GPREG_PHY_READY,
563+ 200, MCD_PHY_POWER_STATUS_TIMEOUT);
564+ if (ret)
565+ dev_err(dev, "PHY%d ready ack timeout", id);
566+
567+ return ret;
568+}
569+
570+static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys)
571+{
572+ struct ipu6_bus_device *adev = isys->adev;
573+ struct ipu6_device *isp = adev->isp;
574+ void __iomem *isp_base = isp->base;
575+ struct sensor_async_sd *s_asd;
576+ struct v4l2_async_connection *asc;
577+ void __iomem *phy_base;
578+ unsigned int i;
579+ u8 phy_id;
580+
581+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
582+ s_asd = container_of(asc, struct sensor_async_sd, asc);
583+ phy_id = s_asd->csi2.port / 4;
584+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
585+
586+ for (i = 0; i < ARRAY_SIZE(common_init_regs); i++)
587+ writel(common_init_regs[i].val,
588+ phy_base + common_init_regs[i].reg);
589+ }
590+}
591+
592+static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg)
593+{
594+ int phy_port;
595+ int ret;
596+
597+ if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1))
598+ return -EINVAL;
599+
600+ /* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */
601+ /* normalize driver port number */
602+ phy_port = cfg->port % 4;
603+
604+ /* swap port number only for A and B */
605+ if (phy_port == 0)
606+ phy_port = 1;
607+ else if (phy_port == 1)
608+ phy_port = 0;
609+
610+ ret = phy_port;
611+
612+ /* check validity per lane configuration */
613+ if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2))
614+ ret = -EINVAL;
615+ else if ((cfg->nlanes == 2 || cfg->nlanes == 1) &&
616+ !(phy_port >= 0 && phy_port <= 3))
617+ ret = -EINVAL;
618+
619+ return ret;
620+}
621+
622+static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys)
623+{
624+ struct device *dev = &isys->adev->auxdev.dev;
625+ struct ipu6_bus_device *adev = isys->adev;
626+ const struct phy_reg **phy_config_regs;
627+ struct ipu6_device *isp = adev->isp;
628+ void __iomem *isp_base = isp->base;
629+ struct sensor_async_sd *s_asd;
630+ struct ipu6_isys_csi2_config cfg;
631+ struct v4l2_async_connection *asc;
632+ int phy_port, phy_id;
633+ unsigned int i;
634+ void __iomem *phy_base;
635+
636+ list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
637+ s_asd = container_of(asc, struct sensor_async_sd, asc);
638+ cfg.port = s_asd->csi2.port;
639+ cfg.nlanes = s_asd->csi2.nlanes;
640+ phy_port = ipu6_isys_driver_port_to_phy_port(&cfg);
641+ if (phy_port < 0) {
642+ dev_err(dev, "invalid port %d for lane %d", cfg.port,
643+ cfg.nlanes);
644+ return -ENXIO;
645+ }
646+
647+ phy_id = cfg.port / 4;
648+ phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
649+ dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id,
650+ cfg.nlanes);
651+
652+ phy_config_regs = config_regs[cfg.nlanes / 2];
653+ cfg.port = phy_port;
654+ for (i = 0; phy_config_regs[cfg.port][i].reg; i++)
655+ writel(phy_config_regs[cfg.port][i].val,
656+ phy_base + phy_config_regs[cfg.port][i].reg);
657+ }
658+
659+ return 0;
660+}
661+
662+#define CSI_MCD_PHY_NUM 2
663+static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM];
664+
665+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
666+ struct ipu6_isys_csi2_config *cfg,
667+ const struct ipu6_isys_csi2_timing *timing,
668+ bool on)
669+{
670+ struct device *dev = &isys->adev->auxdev.dev;
671+ void __iomem *isys_base = isys->pdata->base;
672+ u8 port, phy_id;
673+ refcount_t *ref;
674+ int ret;
675+
676+ port = cfg->port;
677+ phy_id = port / 4;
678+
679+ ref = &phy_power_ref_count[phy_id];
680+
681+ dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port,
682+ cfg->nlanes);
683+
684+ if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
685+ dev_warn(dev, "invalid port ID %d\n", port);
686+ return -EINVAL;
687+ }
688+
689+ if (on) {
690+ if (refcount_read(ref)) {
691+ dev_dbg(dev, "for phy %d is already UP", phy_id);
692+ refcount_inc(ref);
693+ return 0;
694+ }
695+
696+ ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id);
697+ if (ret)
698+ return ret;
699+
700+ ipu6_isys_mcd_phy_reset(isys, phy_id, 0);
701+ ipu6_isys_mcd_phy_common_init(isys);
702+
703+ ret = ipu6_isys_mcd_phy_config(isys);
704+ if (ret)
705+ return ret;
706+
707+ ipu6_isys_mcd_phy_reset(isys, phy_id, 1);
708+ ret = ipu6_isys_mcd_phy_ready(isys, phy_id);
709+ if (ret)
710+ return ret;
711+
712+ refcount_set(ref, 1);
713+ return 0;
714+ }
715+
716+ if (!refcount_dec_and_test(ref))
717+ return 0;
718+
719+ return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id);
720+}
drivers/media/pci/intel/ipu6/ipu6-isys-queue.c+810−0
@@ -0,0 +1,810 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+#include <linux/atomic.h>
6+#include <linux/bug.h>
7+#include <linux/device.h>
8+#include <linux/list.h>
9+#include <linux/lockdep.h>
10+#include <linux/mutex.h>
11+#include <linux/spinlock.h>
12+#include <linux/types.h>
13+
14+#include <media/media-entity.h>
15+#include <media/v4l2-subdev.h>
16+#include <media/videobuf2-dma-contig.h>
17+#include <media/videobuf2-v4l2.h>
18+
19+#include "ipu6-bus.h"
20+#include "ipu6-fw-isys.h"
21+#include "ipu6-isys.h"
22+#include "ipu6-isys-video.h"
23+
24+static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
25+ unsigned int *num_planes, unsigned int sizes[],
26+ struct device *alloc_devs[])
27+{
28+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
29+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
30+ struct device *dev = &av->isys->adev->auxdev.dev;
31+ u32 size = ipu6_isys_get_data_size(av);
32+
33+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */
34+ if (!*num_planes) {
35+ sizes[0] = size;
36+ } else if (sizes[0] < size) {
37+ dev_dbg(dev, "%s: queue setup: size %u < %u\n",
38+ av->vdev.name, sizes[0], size);
39+ return -EINVAL;
40+ }
41+
42+ *num_planes = 1;
43+
44+ return 0;
45+}
46+
47+static int ipu6_isys_buf_prepare(struct vb2_buffer *vb)
48+{
49+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
50+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
51+ struct device *dev = &av->isys->adev->auxdev.dev;
52+ u32 bytesperline = ipu6_isys_get_bytes_per_line(av);
53+ u32 height = ipu6_isys_get_frame_height(av);
54+ u32 size = ipu6_isys_get_data_size(av);
55+
56+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
57+ av->vdev.name, size, vb2_plane_size(vb, 0));
58+
59+ if (size > vb2_plane_size(vb, 0))
60+ return -EINVAL;
61+
62+ vb2_set_plane_payload(vb, 0, bytesperline * height);
63+
64+ return 0;
65+}
66+
67+/*
68+ * Queue a buffer list back to incoming or active queues. The buffers
69+ * are removed from the buffer list.
70+ */
71+void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
72+ unsigned long op_flags,
73+ enum vb2_buffer_state state)
74+{
75+ struct ipu6_isys_buffer *ib, *ib_safe;
76+ unsigned long flags;
77+ bool first = true;
78+
79+ if (!bl)
80+ return;
81+
82+ WARN_ON_ONCE(!bl->nbufs);
83+ WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE &&
84+ op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING);
85+
86+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
87+ struct ipu6_isys_video *av;
88+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
89+ struct ipu6_isys_queue *aq =
90+ vb2_queue_to_isys_queue(vb->vb2_queue);
91+ struct device *dev;
92+
93+ av = ipu6_isys_queue_to_video(aq);
94+ dev = &av->isys->adev->auxdev.dev;
95+ spin_lock_irqsave(&aq->lock, flags);
96+ list_del(&ib->head);
97+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE)
98+ list_add(&ib->head, &aq->active);
99+ else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING)
100+ list_add_tail(&ib->head, &aq->incoming);
101+ spin_unlock_irqrestore(&aq->lock, flags);
102+
103+ if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE)
104+ vb2_buffer_done(vb, state);
105+
106+ if (first) {
107+ dev_dbg(dev,
108+ "queue buf list %p flags %lx, s %d, %d bufs\n",
109+ bl, op_flags, state, bl->nbufs);
110+ first = false;
111+ }
112+
113+ bl->nbufs--;
114+ }
115+
116+ WARN_ON(bl->nbufs);
117+}
118+
119+/*
120+ * flush_firmware_streamon_fail() - Flush in cases where requests may
121+ * have been queued to firmware and the *firmware streamon fails for a
122+ * reason or another.
123+ */
124+static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream)
125+{
126+ struct device *dev = &stream->isys->adev->auxdev.dev;
127+ struct ipu6_isys_queue *aq;
128+ unsigned long flags;
129+
130+ lockdep_assert_held(&stream->mutex);
131+
132+ list_for_each_entry(aq, &stream->queues, node) {
133+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
134+ struct ipu6_isys_buffer *ib, *ib_safe;
135+
136+ spin_lock_irqsave(&aq->lock, flags);
137+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
138+ struct vb2_buffer *vb =
139+ ipu6_isys_buffer_to_vb2_buffer(ib);
140+
141+ list_del(&ib->head);
142+ if (av->streaming) {
143+ dev_dbg(dev,
144+ "%s: queue buffer %u back to incoming\n",
145+ av->vdev.name, vb->index);
146+ /* Queue already streaming, return to driver. */
147+ list_add(&ib->head, &aq->incoming);
148+ continue;
149+ }
150+ /* Queue not yet streaming, return to user. */
151+ dev_dbg(dev, "%s: return %u back to videobuf2\n",
152+ av->vdev.name, vb->index);
153+ vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib),
154+ VB2_BUF_STATE_QUEUED);
155+ }
156+ spin_unlock_irqrestore(&aq->lock, flags);
157+ }
158+}
159+
160+/*
161+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
162+ * that contains one entry from each video buffer queue. If a buffer can't be
163+ * obtained from every queue, the buffers are returned back to the queue.
164+ */
165+static int buffer_list_get(struct ipu6_isys_stream *stream,
166+ struct ipu6_isys_buffer_list *bl)
167+{
168+ struct device *dev = &stream->isys->adev->auxdev.dev;
169+ struct ipu6_isys_queue *aq;
170+ unsigned long flags;
171+ unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING;
172+
173+ bl->nbufs = 0;
174+ INIT_LIST_HEAD(&bl->head);
175+
176+ list_for_each_entry(aq, &stream->queues, node) {
177+ struct ipu6_isys_buffer *ib;
178+
179+ spin_lock_irqsave(&aq->lock, flags);
180+ if (list_empty(&aq->incoming)) {
181+ spin_unlock_irqrestore(&aq->lock, flags);
182+ if (!list_empty(&bl->head))
183+ ipu6_isys_buffer_list_queue(bl, buf_flag, 0);
184+ return -ENODATA;
185+ }
186+
187+ ib = list_last_entry(&aq->incoming,
188+ struct ipu6_isys_buffer, head);
189+
190+ dev_dbg(dev, "buffer: %s: buffer %u\n",
191+ ipu6_isys_queue_to_video(aq)->vdev.name,
192+ ipu6_isys_buffer_to_vb2_buffer(ib)->index);
193+ list_del(&ib->head);
194+ list_add(&ib->head, &bl->head);
195+ spin_unlock_irqrestore(&aq->lock, flags);
196+
197+ bl->nbufs++;
198+ }
199+
200+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
201+
202+ return 0;
203+}
204+
205+static void
206+ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
207+ struct ipu6_fw_isys_frame_buff_set_abi *set)
208+{
209+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
210+
211+ set->output_pins[aq->fw_output].addr =
212+ vb2_dma_contig_plane_dma_addr(vb, 0);
213+ set->output_pins[aq->fw_output].out_buf_id = vb->index + 1;
214+}
215+
216+/*
217+ * Convert a buffer list to a isys fw ABI framebuffer set. The
218+ * buffer list is not modified.
219+ */
220+#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30)
221+void
222+ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
223+ struct ipu6_isys_stream *stream,
224+ struct ipu6_isys_buffer_list *bl)
225+{
226+ struct ipu6_isys_buffer *ib;
227+
228+ WARN_ON(!bl->nbufs);
229+
230+ set->send_irq_sof = 1;
231+ set->send_resp_sof = 1;
232+ set->send_irq_eof = 0;
233+ set->send_resp_eof = 0;
234+
235+ if (stream->streaming)
236+ set->send_irq_capture_ack = 0;
237+ else
238+ set->send_irq_capture_ack = 1;
239+ set->send_irq_capture_done = 0;
240+
241+ set->send_resp_capture_ack = 1;
242+ set->send_resp_capture_done = 1;
243+ if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) {
244+ set->send_resp_capture_ack = 0;
245+ set->send_resp_capture_done = 0;
246+ }
247+
248+ list_for_each_entry(ib, &bl->head, head) {
249+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
250+
251+ ipu6_isys_buf_to_fw_frame_buf_pin(vb, set);
252+ }
253+}
254+
255+/* Start streaming for real. The buffer list must be available. */
256+static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
257+ struct ipu6_isys_buffer_list *bl, bool error)
258+{
259+ struct ipu6_isys_stream *stream = av->stream;
260+ struct device *dev = &stream->isys->adev->auxdev.dev;
261+ struct ipu6_isys_buffer_list __bl;
262+ int ret;
263+
264+ mutex_lock(&stream->isys->stream_mutex);
265+ ret = ipu6_isys_video_set_streaming(av, 1, bl);
266+ mutex_unlock(&stream->isys->stream_mutex);
267+ if (ret)
268+ goto out_requeue;
269+
270+ stream->streaming = 1;
271+
272+ bl = &__bl;
273+
274+ do {
275+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
276+ struct isys_fw_msgs *msg;
277+ u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE;
278+
279+ ret = buffer_list_get(stream, bl);
280+ if (ret < 0)
281+ break;
282+
283+ msg = ipu6_get_fw_msg_buf(stream);
284+ if (!msg)
285+ return -ENOMEM;
286+
287+ buf = &msg->fw_msg.frame;
288+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
289+ ipu6_fw_isys_dump_frame_buff_set(dev, buf,
290+ stream->nr_output_pins);
291+ ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE,
292+ 0);
293+ ret = ipu6_fw_isys_complex_cmd(stream->isys,
294+ stream->stream_handle, buf,
295+ msg->dma_addr, sizeof(*buf),
296+ send_type);
297+ } while (!WARN_ON(ret));
298+
299+ return 0;
300+
301+out_requeue:
302+ if (bl && bl->nbufs)
303+ ipu6_isys_buffer_list_queue(bl,
304+ (IPU6_ISYS_BUFFER_LIST_FL_INCOMING |
305+ error) ?
306+ IPU6_ISYS_BUFFER_LIST_FL_SET_STATE :
307+ 0, error ? VB2_BUF_STATE_ERROR :
308+ VB2_BUF_STATE_QUEUED);
309+ flush_firmware_streamon_fail(stream);
310+
311+ return ret;
312+}
313+
314+static void buf_queue(struct vb2_buffer *vb)
315+{
316+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
317+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
318+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
319+ struct ipu6_isys_video_buffer *ivb =
320+ vb2_buffer_to_ipu6_isys_video_buffer(vvb);
321+ struct ipu6_isys_buffer *ib = &ivb->ib;
322+ struct device *dev = &av->isys->adev->auxdev.dev;
323+ struct media_pipeline *media_pipe =
324+ media_entity_pipeline(&av->vdev.entity);
325+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
326+ struct ipu6_isys_stream *stream = av->stream;
327+ struct ipu6_isys_buffer_list bl;
328+ struct isys_fw_msgs *msg;
329+ unsigned long flags;
330+ dma_addr_t dma;
331+ int ret;
332+
333+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
334+
335+ dma = vb2_dma_contig_plane_dma_addr(vb, 0);
336+ dev_dbg(dev, "iova: iova %pad\n", &dma);
337+
338+ spin_lock_irqsave(&aq->lock, flags);
339+ list_add(&ib->head, &aq->incoming);
340+ spin_unlock_irqrestore(&aq->lock, flags);
341+
342+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
343+ dev_dbg(dev, "media pipeline is not ready for %s\n",
344+ av->vdev.name);
345+ return;
346+ }
347+
348+ mutex_lock(&stream->mutex);
349+
350+ if (stream->nr_streaming != stream->nr_queues) {
351+ dev_dbg(dev, "not streaming yet, adding to incoming\n");
352+ goto out;
353+ }
354+
355+ /*
356+ * We just put one buffer to the incoming list of this queue
357+ * (above). Let's see whether all queues in the pipeline would
358+ * have a buffer.
359+ */
360+ ret = buffer_list_get(stream, &bl);
361+ if (ret < 0) {
362+ dev_dbg(dev, "No buffers available\n");
363+ goto out;
364+ }
365+
366+ msg = ipu6_get_fw_msg_buf(stream);
367+ if (!msg) {
368+ ret = -ENOMEM;
369+ goto out;
370+ }
371+
372+ buf = &msg->fw_msg.frame;
373+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl);
374+ ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
375+
376+ if (!stream->streaming) {
377+ ret = ipu6_isys_stream_start(av, &bl, true);
378+ if (ret)
379+ dev_err(dev, "stream start failed.\n");
380+ goto out;
381+ }
382+
383+ /*
384+ * We must queue the buffers in the buffer list to the
385+ * appropriate video buffer queues BEFORE passing them to the
386+ * firmware since we could get a buffer event back before we
387+ * have queued them ourselves to the active queue.
388+ */
389+ ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
390+
391+ ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
392+ buf, msg->dma_addr, sizeof(*buf),
393+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE);
394+ if (ret < 0)
395+ dev_err(dev, "send stream capture failed\n");
396+
397+out:
398+ mutex_unlock(&stream->mutex);
399+}
400+
401+static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
402+{
403+ struct v4l2_mbus_framefmt format;
404+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
405+ struct device *dev = &av->isys->adev->auxdev.dev;
406+ struct media_pad *remote_pad =
407+ media_pad_remote_pad_first(av->vdev.entity.pads);
408+ struct v4l2_subdev *sd;
409+ u32 r_stream, code;
410+ int ret;
411+
412+ if (!remote_pad)
413+ return -ENOTCONN;
414+
415+ sd = media_entity_to_v4l2_subdev(remote_pad->entity);
416+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
417+
418+ ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
419+ &format);
420+
421+ if (ret) {
422+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
423+ sd->entity.name, remote_pad->index, r_stream);
424+ return ret;
425+ }
426+
427+ if (format.width != ipu6_isys_get_frame_width(av) ||
428+ format.height != ipu6_isys_get_frame_height(av)) {
429+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
430+ ipu6_isys_get_frame_width(av),
431+ ipu6_isys_get_frame_height(av), format.width,
432+ format.height);
433+ return -EINVAL;
434+ }
435+
436+ code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;
437+ if (format.code != code) {
438+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
439+ code, format.code);
440+ return -EINVAL;
441+ }
442+
443+ return 0;
444+}
445+
446+static void return_buffers(struct ipu6_isys_queue *aq,
447+ enum vb2_buffer_state state)
448+{
449+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
450+ struct ipu6_isys_buffer *ib;
451+ bool need_reset = false;
452+ unsigned long flags;
453+
454+ spin_lock_irqsave(&aq->lock, flags);
455+ while (!list_empty(&aq->incoming)) {
456+ struct vb2_buffer *vb;
457+
458+ ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer,
459+ head);
460+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
461+ list_del(&ib->head);
462+ spin_unlock_irqrestore(&aq->lock, flags);
463+
464+ vb2_buffer_done(vb, state);
465+
466+ spin_lock_irqsave(&aq->lock, flags);
467+ }
468+
469+ /*
470+ * Something went wrong (FW crash / HW hang / not all buffers
471+ * returned from isys) if there are still buffers queued in active
472+ * queue. We have to clean up places a bit.
473+ */
474+ while (!list_empty(&aq->active)) {
475+ struct vb2_buffer *vb;
476+
477+ ib = list_first_entry(&aq->active, struct ipu6_isys_buffer,
478+ head);
479+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
480+
481+ list_del(&ib->head);
482+ spin_unlock_irqrestore(&aq->lock, flags);
483+
484+ vb2_buffer_done(vb, state);
485+
486+ spin_lock_irqsave(&aq->lock, flags);
487+ need_reset = true;
488+ }
489+
490+ spin_unlock_irqrestore(&aq->lock, flags);
491+
492+ if (need_reset) {
493+ mutex_lock(&av->isys->mutex);
494+ av->isys->need_reset = true;
495+ mutex_unlock(&av->isys->mutex);
496+ }
497+}
498+
499+static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av)
500+{
501+ video_device_pipeline_stop(&av->vdev);
502+ ipu6_isys_put_stream(av->stream);
503+ av->stream = NULL;
504+}
505+
506+static int start_streaming(struct vb2_queue *q, unsigned int count)
507+{
508+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
509+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
510+ struct device *dev = &av->isys->adev->auxdev.dev;
511+ const struct ipu6_isys_pixelformat *pfmt =
512+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
513+ struct ipu6_isys_buffer_list __bl, *bl = NULL;
514+ struct ipu6_isys_stream *stream;
515+ struct media_entity *source_entity = NULL;
516+ int nr_queues, ret;
517+
518+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
519+ av->vdev.name, ipu6_isys_get_frame_width(av),
520+ ipu6_isys_get_frame_height(av), pfmt->css_pixelformat);
521+
522+ ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
523+ if (ret < 0) {
524+ dev_dbg(dev, "failed to setup video\n");
525+ goto out_return_buffers;
526+ }
527+
528+ ret = ipu6_isys_link_fmt_validate(aq);
529+ if (ret) {
530+ dev_dbg(dev,
531+ "%s: link format validation failed (%d)\n",
532+ av->vdev.name, ret);
533+ goto out_pipeline_stop;
534+ }
535+
536+ ret = ipu6_isys_fw_open(av->isys);
537+ if (ret)
538+ goto out_pipeline_stop;
539+
540+ stream = av->stream;
541+ mutex_lock(&stream->mutex);
542+ if (!stream->nr_streaming) {
543+ ret = ipu6_isys_video_prepare_stream(av, source_entity,
544+ nr_queues);
545+ if (ret)
546+ goto out_fw_close;
547+ }
548+
549+ stream->nr_streaming++;
550+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming,
551+ stream->nr_queues);
552+
553+ list_add(&aq->node, &stream->queues);
554+ ipu6_isys_set_csi2_streams_status(av, true);
555+ ipu6_isys_configure_stream_watermark(av, true);
556+ ipu6_isys_update_stream_watermark(av, true);
557+
558+ if (stream->nr_streaming != stream->nr_queues)
559+ goto out;
560+
561+ bl = &__bl;
562+ ret = buffer_list_get(stream, bl);
563+ if (ret < 0) {
564+ dev_warn(dev, "no buffer available, DRIVER BUG?\n");
565+ goto out;
566+ }
567+
568+ ret = ipu6_isys_stream_start(av, bl, false);
569+ if (ret)
570+ goto out_stream_start;
571+
572+out:
573+ mutex_unlock(&stream->mutex);
574+
575+ return 0;
576+
577+out_stream_start:
578+ ipu6_isys_update_stream_watermark(av, false);
579+ list_del(&aq->node);
580+ stream->nr_streaming--;
581+
582+out_fw_close:
583+ mutex_unlock(&stream->mutex);
584+ ipu6_isys_fw_close(av->isys);
585+
586+out_pipeline_stop:
587+ ipu6_isys_stream_cleanup(av);
588+
589+out_return_buffers:
590+ return_buffers(aq, VB2_BUF_STATE_QUEUED);
591+
592+ return ret;
593+}
594+
595+static void stop_streaming(struct vb2_queue *q)
596+{
597+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
598+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
599+ struct ipu6_isys_stream *stream = av->stream;
600+
601+ ipu6_isys_set_csi2_streams_status(av, false);
602+
603+ mutex_lock(&stream->mutex);
604+
605+ ipu6_isys_update_stream_watermark(av, false);
606+
607+ mutex_lock(&av->isys->stream_mutex);
608+ if (stream->nr_streaming == stream->nr_queues && stream->streaming)
609+ ipu6_isys_video_set_streaming(av, 0, NULL);
610+ mutex_unlock(&av->isys->stream_mutex);
611+
612+ stream->nr_streaming--;
613+ list_del(&aq->node);
614+ stream->streaming = 0;
615+ mutex_unlock(&stream->mutex);
616+
617+ ipu6_isys_stream_cleanup(av);
618+
619+ return_buffers(aq, VB2_BUF_STATE_ERROR);
620+
621+ ipu6_isys_fw_close(av->isys);
622+}
623+
624+static unsigned int
625+get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream,
626+ struct ipu6_fw_isys_resp_info_abi *info)
627+{
628+ u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0];
629+ struct ipu6_isys *isys = stream->isys;
630+ struct device *dev = &isys->adev->auxdev.dev;
631+ unsigned int i;
632+
633+ /*
634+ * The timestamp is invalid as no TSC in some FPGA platform,
635+ * so get the sequence from pipeline directly in this case.
636+ */
637+ if (time == 0)
638+ return atomic_read(&stream->sequence) - 1;
639+
640+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
641+ if (time == stream->seq[i].timestamp) {
642+ dev_dbg(dev, "sof: using seq nr %u for ts %llu\n",
643+ stream->seq[i].sequence, time);
644+ return stream->seq[i].sequence;
645+ }
646+
647+ for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
648+ dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n",
649+ stream->seq[i].sequence, stream->seq[i].timestamp);
650+
651+ return 0;
652+}
653+
654+static u64 get_sof_ns_delta(struct ipu6_isys_video *av,
655+ struct ipu6_fw_isys_resp_info_abi *info)
656+{
657+ struct ipu6_bus_device *adev = av->isys->adev;
658+ struct ipu6_device *isp = adev->isp;
659+ u64 delta, tsc_now;
660+
661+ ipu6_buttress_tsc_read(isp, &tsc_now);
662+ if (!tsc_now)
663+ return 0;
664+
665+ delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
666+
667+ return ipu6_buttress_tsc_ticks_to_ns(delta, isp);
668+}
669+
670+void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
671+ struct ipu6_fw_isys_resp_info_abi *info)
672+{
673+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
674+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
675+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
676+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
677+ struct device *dev = &av->isys->adev->auxdev.dev;
678+ struct ipu6_isys_stream *stream = av->stream;
679+ u64 ns;
680+ u32 sequence;
681+
682+ ns = ktime_get_ns() - get_sof_ns_delta(av, info);
683+ sequence = get_sof_sequence_by_timestamp(stream, info);
684+
685+ vbuf->vb2_buf.timestamp = ns;
686+ vbuf->sequence = sequence;
687+
688+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
689+ av->vdev.name, ktime_get_ns(), sequence);
690+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
691+ vbuf->vb2_buf.timestamp);
692+}
693+
694+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib)
695+{
696+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
697+
698+ if (atomic_read(&ib->str2mmio_flag)) {
699+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
700+ /*
701+ * Operation on buffer is ended with error and will be reported
702+ * to the userspace when it is de-queued
703+ */
704+ atomic_set(&ib->str2mmio_flag, 0);
705+ } else {
706+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
707+ }
708+}
709+
710+void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
711+ struct ipu6_fw_isys_resp_info_abi *info)
712+{
713+ struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq;
714+ struct ipu6_isys *isys = stream->isys;
715+ struct device *dev = &isys->adev->auxdev.dev;
716+ struct ipu6_isys_buffer *ib;
717+ struct vb2_buffer *vb;
718+ unsigned long flags;
719+ bool first = true;
720+ struct vb2_v4l2_buffer *buf;
721+
722+ spin_lock_irqsave(&aq->lock, flags);
723+ if (list_empty(&aq->active)) {
724+ spin_unlock_irqrestore(&aq->lock, flags);
725+ dev_err(dev, "active queue empty\n");
726+ return;
727+ }
728+
729+ list_for_each_entry_reverse(ib, &aq->active, head) {
730+ dma_addr_t addr;
731+
732+ vb = ipu6_isys_buffer_to_vb2_buffer(ib);
733+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
734+
735+ if (info->pin.addr != addr) {
736+ if (first)
737+ dev_err(dev, "Unexpected buffer address %pad\n",
738+ &addr);
739+ first = false;
740+ continue;
741+ }
742+
743+ if (info->error_info.error ==
744+ IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) {
745+ /*
746+ * Check for error message:
747+ * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO'
748+ */
749+ atomic_set(&ib->str2mmio_flag, 1);
750+ }
751+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
752+
753+ buf = to_vb2_v4l2_buffer(vb);
754+ buf->field = V4L2_FIELD_NONE;
755+
756+ list_del(&ib->head);
757+ spin_unlock_irqrestore(&aq->lock, flags);
758+
759+ ipu6_isys_buf_calc_sequence_time(ib, info);
760+
761+ ipu6_isys_queue_buf_done(ib);
762+
763+ return;
764+ }
765+
766+ dev_err(dev, "Failed to find a matching video buffer");
767+
768+ spin_unlock_irqrestore(&aq->lock, flags);
769+}
770+
771+static const struct vb2_ops ipu6_isys_queue_ops = {
772+ .queue_setup = queue_setup,
773+ .wait_prepare = vb2_ops_wait_prepare,
774+ .wait_finish = vb2_ops_wait_finish,
775+ .buf_prepare = ipu6_isys_buf_prepare,
776+ .start_streaming = start_streaming,
777+ .stop_streaming = stop_streaming,
778+ .buf_queue = buf_queue,
779+};
780+
781+int ipu6_isys_queue_init(struct ipu6_isys_queue *aq)
782+{
783+ struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys;
784+ struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
785+ int ret;
786+
787+ /* no support for userptr */
788+ if (!aq->vbq.io_modes)
789+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
790+
791+ aq->vbq.drv_priv = aq;
792+ aq->vbq.ops = &ipu6_isys_queue_ops;
793+ aq->vbq.lock = &av->mutex;
794+ aq->vbq.mem_ops = &vb2_dma_contig_memops;
795+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
796+ aq->vbq.min_queued_buffers = 1;
797+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
798+
799+ ret = vb2_queue_init(&aq->vbq);
800+ if (ret)
801+ return ret;
802+
803+ aq->dev = &isys->adev->auxdev.dev;
804+ aq->vbq.dev = &isys->adev->auxdev.dev;
805+ spin_lock_init(&aq->lock);
806+ INIT_LIST_HEAD(&aq->active);
807+ INIT_LIST_HEAD(&aq->incoming);
808+
809+ return 0;
810+}
drivers/media/pci/intel/ipu6/ipu6-isys-queue.h+78−0
@@ -0,0 +1,78 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_ISYS_QUEUE_H
5+#define IPU6_ISYS_QUEUE_H
6+
7+#include <linux/container_of.h>
8+#include <linux/atomic.h>
9+#include <linux/device.h>
10+#include <linux/list.h>
11+#include <linux/spinlock_types.h>
12+
13+#include <media/videobuf2-v4l2.h>
14+
15+#include "ipu6-fw-isys.h"
16+#include "ipu6-isys-video.h"
17+
18+struct ipu6_isys_stream;
19+
20+struct ipu6_isys_queue {
21+ struct vb2_queue vbq;
22+ struct list_head node;
23+ struct device *dev;
24+ /*
25+ * @lock: serialise access to queued and pre_streamon_queued
26+ */
27+ spinlock_t lock;
28+ struct list_head active;
29+ struct list_head incoming;
30+ unsigned int fw_output;
31+};
32+
33+struct ipu6_isys_buffer {
34+ struct list_head head;
35+ atomic_t str2mmio_flag;
36+};
37+
38+struct ipu6_isys_video_buffer {
39+ struct vb2_v4l2_buffer vb_v4l2;
40+ struct ipu6_isys_buffer ib;
41+};
42+
43+#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0)
44+#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1)
45+#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2)
46+
47+struct ipu6_isys_buffer_list {
48+ struct list_head head;
49+ unsigned int nbufs;
50+};
51+
52+#define vb2_queue_to_isys_queue(__vb2) \
53+ container_of(__vb2, struct ipu6_isys_queue, vbq)
54+
55+#define ipu6_isys_to_isys_video_buffer(__ib) \
56+ container_of(__ib, struct ipu6_isys_video_buffer, ib)
57+
58+#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \
59+ container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2)
60+
61+#define ipu6_isys_buffer_to_vb2_buffer(__ib) \
62+ (&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
63+
64+void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
65+ unsigned long op_flags,
66+ enum vb2_buffer_state state);
67+void
68+ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
69+ struct ipu6_isys_stream *stream,
70+ struct ipu6_isys_buffer_list *bl);
71+void
72+ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
73+ struct ipu6_fw_isys_resp_info_abi *info);
74+void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib);
75+void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
76+ struct ipu6_fw_isys_resp_info_abi *info);
77+int ipu6_isys_queue_init(struct ipu6_isys_queue *aq);
78+#endif /* IPU6_ISYS_QUEUE_H */
drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c+403−0
@@ -0,0 +1,403 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bug.h>
7+#include <linux/device.h>
8+#include <linux/minmax.h>
9+
10+#include <media/media-entity.h>
11+#include <media/mipi-csi2.h>
12+#include <media/v4l2-ctrls.h>
13+#include <media/v4l2-subdev.h>
14+
15+#include "ipu6-bus.h"
16+#include "ipu6-isys.h"
17+#include "ipu6-isys-subdev.h"
18+
19+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
20+{
21+ switch (code) {
22+ case MEDIA_BUS_FMT_RGB888_1X24:
23+ case MEDIA_BUS_FMT_META_24:
24+ return 24;
25+ case MEDIA_BUS_FMT_RGB565_1X16:
26+ case MEDIA_BUS_FMT_UYVY8_1X16:
27+ case MEDIA_BUS_FMT_YUYV8_1X16:
28+ case MEDIA_BUS_FMT_META_16:
29+ return 16;
30+ case MEDIA_BUS_FMT_SBGGR12_1X12:
31+ case MEDIA_BUS_FMT_SGBRG12_1X12:
32+ case MEDIA_BUS_FMT_SGRBG12_1X12:
33+ case MEDIA_BUS_FMT_SRGGB12_1X12:
34+ case MEDIA_BUS_FMT_META_12:
35+ return 12;
36+ case MEDIA_BUS_FMT_SBGGR10_1X10:
37+ case MEDIA_BUS_FMT_SGBRG10_1X10:
38+ case MEDIA_BUS_FMT_SGRBG10_1X10:
39+ case MEDIA_BUS_FMT_SRGGB10_1X10:
40+ case MEDIA_BUS_FMT_META_10:
41+ return 10;
42+ case MEDIA_BUS_FMT_SBGGR8_1X8:
43+ case MEDIA_BUS_FMT_SGBRG8_1X8:
44+ case MEDIA_BUS_FMT_SGRBG8_1X8:
45+ case MEDIA_BUS_FMT_SRGGB8_1X8:
46+ case MEDIA_BUS_FMT_META_8:
47+ return 8;
48+ default:
49+ WARN_ON(1);
50+ return 8;
51+ }
52+}
53+
54+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
55+{
56+ switch (code) {
57+ case MEDIA_BUS_FMT_RGB565_1X16:
58+ return MIPI_CSI2_DT_RGB565;
59+ case MEDIA_BUS_FMT_RGB888_1X24:
60+ return MIPI_CSI2_DT_RGB888;
61+ case MEDIA_BUS_FMT_UYVY8_1X16:
62+ case MEDIA_BUS_FMT_YUYV8_1X16:
63+ return MIPI_CSI2_DT_YUV422_8B;
64+ case MEDIA_BUS_FMT_SBGGR16_1X16:
65+ case MEDIA_BUS_FMT_SGBRG16_1X16:
66+ case MEDIA_BUS_FMT_SGRBG16_1X16:
67+ case MEDIA_BUS_FMT_SRGGB16_1X16:
68+ return MIPI_CSI2_DT_RAW16;
69+ case MEDIA_BUS_FMT_SBGGR12_1X12:
70+ case MEDIA_BUS_FMT_SGBRG12_1X12:
71+ case MEDIA_BUS_FMT_SGRBG12_1X12:
72+ case MEDIA_BUS_FMT_SRGGB12_1X12:
73+ return MIPI_CSI2_DT_RAW12;
74+ case MEDIA_BUS_FMT_SBGGR10_1X10:
75+ case MEDIA_BUS_FMT_SGBRG10_1X10:
76+ case MEDIA_BUS_FMT_SGRBG10_1X10:
77+ case MEDIA_BUS_FMT_SRGGB10_1X10:
78+ return MIPI_CSI2_DT_RAW10;
79+ case MEDIA_BUS_FMT_SBGGR8_1X8:
80+ case MEDIA_BUS_FMT_SGBRG8_1X8:
81+ case MEDIA_BUS_FMT_SGRBG8_1X8:
82+ case MEDIA_BUS_FMT_SRGGB8_1X8:
83+ return MIPI_CSI2_DT_RAW8;
84+ default:
85+ /* return unavailable MIPI data type - 0x3f */
86+ WARN_ON(1);
87+ return 0x3f;
88+ }
89+}
90+
91+bool ipu6_isys_is_bayer_format(u32 code)
92+{
93+ switch (ipu6_isys_mbus_code_to_mipi(code)) {
94+ case MIPI_CSI2_DT_RAW8:
95+ case MIPI_CSI2_DT_RAW10:
96+ case MIPI_CSI2_DT_RAW12:
97+ case MIPI_CSI2_DT_RAW14:
98+ case MIPI_CSI2_DT_RAW16:
99+ case MIPI_CSI2_DT_RAW20:
100+ case MIPI_CSI2_DT_RAW24:
101+ case MIPI_CSI2_DT_RAW28:
102+ return true;
103+ default:
104+ return false;
105+ }
106+}
107+
108+u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
109+{
110+ static const u32 code_map[] = {
111+ MEDIA_BUS_FMT_SRGGB8_1X8,
112+ MEDIA_BUS_FMT_SGRBG8_1X8,
113+ MEDIA_BUS_FMT_SGBRG8_1X8,
114+ MEDIA_BUS_FMT_SBGGR8_1X8,
115+ MEDIA_BUS_FMT_SRGGB10_1X10,
116+ MEDIA_BUS_FMT_SGRBG10_1X10,
117+ MEDIA_BUS_FMT_SGBRG10_1X10,
118+ MEDIA_BUS_FMT_SBGGR10_1X10,
119+ MEDIA_BUS_FMT_SRGGB12_1X12,
120+ MEDIA_BUS_FMT_SGRBG12_1X12,
121+ MEDIA_BUS_FMT_SGBRG12_1X12,
122+ MEDIA_BUS_FMT_SBGGR12_1X12,
123+ MEDIA_BUS_FMT_SRGGB16_1X16,
124+ MEDIA_BUS_FMT_SGRBG16_1X16,
125+ MEDIA_BUS_FMT_SGBRG16_1X16,
126+ MEDIA_BUS_FMT_SBGGR16_1X16,
127+ };
128+ u32 i;
129+
130+ for (i = 0; i < ARRAY_SIZE(code_map); i++)
131+ if (code_map[i] == code)
132+ break;
133+
134+ if (WARN_ON(i == ARRAY_SIZE(code_map)))
135+ return code;
136+
137+ return code_map[i ^ (((y & 1) << 1) | (x & 1))];
138+}
139+
140+int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
141+ struct v4l2_subdev_state *state,
142+ struct v4l2_subdev_format *format)
143+{
144+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
145+ struct v4l2_mbus_framefmt *fmt;
146+ struct v4l2_rect *crop;
147+ u32 code = asd->supported_codes[0];
148+ u32 other_pad, other_stream;
149+ unsigned int i;
150+ int ret;
151+
152+ /* No transcoding, source and sink formats must match. */
153+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
154+ sd->entity.num_pads > 1)
155+ return v4l2_subdev_get_fmt(sd, state, format);
156+
157+ format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,
158+ IPU6_ISYS_MAX_WIDTH);
159+ format->format.height = clamp(format->format.height,
160+ IPU6_ISYS_MIN_HEIGHT,
161+ IPU6_ISYS_MAX_HEIGHT);
162+
163+ for (i = 0; asd->supported_codes[i]; i++) {
164+ if (asd->supported_codes[i] == format->format.code) {
165+ code = asd->supported_codes[i];
166+ break;
167+ }
168+ }
169+ format->format.code = code;
170+ format->format.field = V4L2_FIELD_NONE;
171+
172+ /* Store the format and propagate it to the source pad. */
173+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
174+ if (!fmt)
175+ return -EINVAL;
176+
177+ *fmt = format->format;
178+
179+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
180+ return 0;
181+
182+ /* propagate format to following source pad */
183+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
184+ format->stream);
185+ if (!fmt)
186+ return -EINVAL;
187+
188+ *fmt = format->format;
189+
190+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
191+ format->pad,
192+ format->stream,
193+ &other_pad,
194+ &other_stream);
195+ if (ret)
196+ return -EINVAL;
197+
198+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
199+ /* reset crop */
200+ crop->left = 0;
201+ crop->top = 0;
202+ crop->width = fmt->width;
203+ crop->height = fmt->height;
204+
205+ return 0;
206+}
207+
208+int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
209+ struct v4l2_subdev_state *state,
210+ struct v4l2_subdev_mbus_code_enum *code)
211+{
212+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
213+ const u32 *supported_codes = asd->supported_codes;
214+ u32 index;
215+
216+ for (index = 0; supported_codes[index]; index++) {
217+ if (index == code->index) {
218+ code->code = supported_codes[index];
219+ return 0;
220+ }
221+ }
222+
223+ return -EINVAL;
224+}
225+
226+static int subdev_set_routing(struct v4l2_subdev *sd,
227+ struct v4l2_subdev_state *state,
228+ struct v4l2_subdev_krouting *routing)
229+{
230+ static const struct v4l2_mbus_framefmt format = {
231+ .width = 4096,
232+ .height = 3072,
233+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
234+ .field = V4L2_FIELD_NONE,
235+ };
236+ int ret;
237+
238+ ret = v4l2_subdev_routing_validate(sd, routing,
239+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
240+ if (ret)
241+ return ret;
242+
243+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
244+}
245+
246+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
247+ struct v4l2_mbus_framefmt *format)
248+{
249+ struct v4l2_mbus_framefmt *fmt;
250+ struct v4l2_subdev_state *state;
251+
252+ if (!sd || !format)
253+ return -EINVAL;
254+
255+ state = v4l2_subdev_lock_and_get_active_state(sd);
256+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
257+ if (fmt)
258+ *format = *fmt;
259+ v4l2_subdev_unlock_state(state);
260+
261+ return fmt ? 0 : -EINVAL;
262+}
263+
264+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
265+ struct v4l2_rect *crop)
266+{
267+ struct v4l2_subdev_state *state;
268+ struct v4l2_rect *rect;
269+
270+ if (!sd || !crop)
271+ return -EINVAL;
272+
273+ state = v4l2_subdev_lock_and_get_active_state(sd);
274+ rect = v4l2_subdev_state_get_crop(state, pad, stream);
275+ if (rect)
276+ *crop = *rect;
277+ v4l2_subdev_unlock_state(state);
278+
279+ return rect ? 0 : -EINVAL;
280+}
281+
282+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
283+{
284+ struct v4l2_subdev_state *state;
285+ struct v4l2_subdev_route *routes;
286+ unsigned int i;
287+ u32 source_stream = 0;
288+
289+ state = v4l2_subdev_lock_and_get_active_state(sd);
290+ if (!state)
291+ return 0;
292+
293+ routes = state->routing.routes;
294+ for (i = 0; i < state->routing.num_routes; i++) {
295+ if (routes[i].source_pad == pad) {
296+ source_stream = routes[i].source_stream;
297+ break;
298+ }
299+ }
300+
301+ v4l2_subdev_unlock_state(state);
302+
303+ return source_stream;
304+}
305+
306+static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,
307+ struct v4l2_subdev_state *state)
308+{
309+ struct v4l2_subdev_route route = {
310+ .sink_pad = 0,
311+ .sink_stream = 0,
312+ .source_pad = 1,
313+ .source_stream = 0,
314+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
315+ };
316+ struct v4l2_subdev_krouting routing = {
317+ .num_routes = 1,
318+ .routes = &route,
319+ };
320+
321+ return subdev_set_routing(sd, state, &routing);
322+}
323+
324+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
325+ struct v4l2_subdev_state *state,
326+ enum v4l2_subdev_format_whence which,
327+ struct v4l2_subdev_krouting *routing)
328+{
329+ return subdev_set_routing(sd, state, routing);
330+}
331+
332+static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {
333+ .init_state = ipu6_isys_subdev_init_state,
334+};
335+
336+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
337+ const struct v4l2_subdev_ops *ops,
338+ unsigned int nr_ctrls,
339+ unsigned int num_sink_pads,
340+ unsigned int num_source_pads)
341+{
342+ unsigned int num_pads = num_sink_pads + num_source_pads;
343+ unsigned int i;
344+ int ret;
345+
346+ v4l2_subdev_init(&asd->sd, ops);
347+
348+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
349+ V4L2_SUBDEV_FL_HAS_EVENTS |
350+ V4L2_SUBDEV_FL_STREAMS;
351+ asd->sd.owner = THIS_MODULE;
352+ asd->sd.dev = &asd->isys->adev->auxdev.dev;
353+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
354+ asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;
355+
356+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
357+ sizeof(*asd->pad), GFP_KERNEL);
358+ if (!asd->pad)
359+ return -ENOMEM;
360+
361+ for (i = 0; i < num_sink_pads; i++)
362+ asd->pad[i].flags = MEDIA_PAD_FL_SINK |
363+ MEDIA_PAD_FL_MUST_CONNECT;
364+
365+ for (i = num_sink_pads; i < num_pads; i++)
366+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
367+
368+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
369+ if (ret)
370+ return ret;
371+
372+ if (asd->ctrl_init) {
373+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
374+ if (ret)
375+ goto out_media_entity_cleanup;
376+
377+ asd->ctrl_init(&asd->sd);
378+ if (asd->ctrl_handler.error) {
379+ ret = asd->ctrl_handler.error;
380+ goto out_v4l2_ctrl_handler_free;
381+ }
382+
383+ asd->sd.ctrl_handler = &asd->ctrl_handler;
384+ }
385+
386+ asd->source = -1;
387+
388+ return 0;
389+
390+out_v4l2_ctrl_handler_free:
391+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
392+
393+out_media_entity_cleanup:
394+ media_entity_cleanup(&asd->sd.entity);
395+
396+ return ret;
397+}
398+
399+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)
400+{
401+ media_entity_cleanup(&asd->sd.entity);
402+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
403+}
drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h+59−0
@@ -0,0 +1,59 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_ISYS_SUBDEV_H
5+#define IPU6_ISYS_SUBDEV_H
6+
7+#include <linux/container_of.h>
8+
9+#include <media/media-entity.h>
10+#include <media/v4l2-ctrls.h>
11+#include <media/v4l2-subdev.h>
12+
13+struct ipu6_isys;
14+
15+struct ipu6_isys_subdev {
16+ struct v4l2_subdev sd;
17+ struct ipu6_isys *isys;
18+ u32 const *supported_codes;
19+ struct media_pad *pad;
20+ struct v4l2_ctrl_handler ctrl_handler;
21+ void (*ctrl_init)(struct v4l2_subdev *sd);
22+ int source; /* SSI stream source; -1 if unset */
23+};
24+
25+#define to_ipu6_isys_subdev(__sd) \
26+ container_of(__sd, struct ipu6_isys_subdev, sd)
27+
28+unsigned int ipu6_isys_mbus_code_to_bpp(u32 code);
29+unsigned int ipu6_isys_mbus_code_to_mipi(u32 code);
30+bool ipu6_isys_is_bayer_format(u32 code);
31+u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y);
32+
33+int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
34+ struct v4l2_subdev_state *state,
35+ struct v4l2_subdev_format *fmt);
36+int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
37+ struct v4l2_subdev_state *state,
38+ struct v4l2_subdev_mbus_code_enum
39+ *code);
40+int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd,
41+ struct media_link *link,
42+ struct v4l2_subdev_format *source_fmt,
43+ struct v4l2_subdev_format *sink_fmt);
44+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
45+int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
46+ struct v4l2_mbus_framefmt *format);
47+int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
48+ struct v4l2_rect *crop);
49+int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
50+ struct v4l2_subdev_state *state,
51+ enum v4l2_subdev_format_whence which,
52+ struct v4l2_subdev_krouting *routing);
53+int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
54+ const struct v4l2_subdev_ops *ops,
55+ unsigned int nr_ctrls,
56+ unsigned int num_sink_pads,
57+ unsigned int num_source_pads);
58+void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd);
59+#endif /* IPU6_ISYS_SUBDEV_H */
drivers/media/pci/intel/ipu6/ipu6-isys-video.c+1420−0
@@ -0,0 +1,1420 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/align.h>
7+#include <linux/bits.h>
8+#include <linux/bug.h>
9+#include <linux/completion.h>
10+#include <linux/container_of.h>
11+#include <linux/device.h>
12+#include <linux/list.h>
13+#include <linux/math64.h>
14+#include <linux/minmax.h>
15+#include <linux/module.h>
16+#include <linux/mutex.h>
17+#include <linux/pm_runtime.h>
18+#include <linux/spinlock.h>
19+#include <linux/string.h>
20+
21+#include <media/media-entity.h>
22+#include <media/v4l2-ctrls.h>
23+#include <media/v4l2-dev.h>
24+#include <media/v4l2-fh.h>
25+#include <media/v4l2-ioctl.h>
26+#include <media/v4l2-subdev.h>
27+#include <media/videobuf2-v4l2.h>
28+
29+#include "ipu6.h"
30+#include "ipu6-bus.h"
31+#include "ipu6-cpd.h"
32+#include "ipu6-fw-isys.h"
33+#include "ipu6-isys.h"
34+#include "ipu6-isys-csi2.h"
35+#include "ipu6-isys-queue.h"
36+#include "ipu6-isys-video.h"
37+#include "ipu6-platform-regs.h"
38+
39+const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
40+ { V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
41+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
42+ { V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
43+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
44+ { V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
45+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
46+ { V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
47+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
48+ { V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
49+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
50+ { V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
51+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
52+ { V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
53+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
54+ { V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
55+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
56+ { V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
57+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
58+ { V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
59+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
60+ { V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
61+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
62+ { V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
63+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
64+ { V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
65+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
66+ { V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
67+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
68+ { V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
69+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
70+ { V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
71+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
72+ { V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
73+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
74+ { V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
75+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
76+ { V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
77+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
78+ { V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
79+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
80+ { V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
81+ IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
82+ { V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
83+ IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
84+ { V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
85+ IPU6_FW_ISYS_FRAME_FORMAT_RGB565 },
86+ { V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
87+ IPU6_FW_ISYS_FRAME_FORMAT_RGBA888 },
88+ { V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8,
89+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8, true },
90+ { V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10,
91+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10, true },
92+ { V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12,
93+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12, true },
94+ { V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16,
95+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16, true },
96+};
97+
98+static int video_open(struct file *file)
99+{
100+ struct ipu6_isys_video *av = video_drvdata(file);
101+ struct ipu6_isys *isys = av->isys;
102+ struct ipu6_bus_device *adev = isys->adev;
103+
104+ mutex_lock(&isys->mutex);
105+ if (isys->need_reset) {
106+ mutex_unlock(&isys->mutex);
107+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n");
108+ return -EIO;
109+ }
110+ mutex_unlock(&isys->mutex);
111+
112+ return v4l2_fh_open(file);
113+}
114+
115+const struct ipu6_isys_pixelformat *
116+ipu6_isys_get_isys_format(u32 pixelformat, u32 type)
117+{
118+ const struct ipu6_isys_pixelformat *default_pfmt = NULL;
119+ unsigned int i;
120+
121+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
122+ const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
123+
124+ if (type && ((!pfmt->is_meta &&
125+ type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
126+ (pfmt->is_meta &&
127+ type != V4L2_BUF_TYPE_META_CAPTURE)))
128+ continue;
129+
130+ if (!default_pfmt)
131+ default_pfmt = pfmt;
132+
133+ if (pfmt->pixelformat != pixelformat)
134+ continue;
135+
136+ return pfmt;
137+ }
138+
139+ return default_pfmt;
140+}
141+
142+static int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
143+ struct v4l2_capability *cap)
144+{
145+ struct ipu6_isys_video *av = video_drvdata(file);
146+
147+ strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));
148+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
149+
150+ return 0;
151+}
152+
153+static int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
154+ struct v4l2_fmtdesc *f)
155+{
156+ unsigned int i, num_found;
157+
158+ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
159+ if ((ipu6_isys_pfmts[i].is_meta &&
160+ f->type != V4L2_BUF_TYPE_META_CAPTURE) ||
161+ (!ipu6_isys_pfmts[i].is_meta &&
162+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
163+ continue;
164+
165+ if (f->mbus_code && f->mbus_code != ipu6_isys_pfmts[i].code)
166+ continue;
167+
168+ if (num_found < f->index) {
169+ num_found++;
170+ continue;
171+ }
172+
173+ f->flags = 0;
174+ f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
175+
176+ return 0;
177+ }
178+
179+ return -EINVAL;
180+}
181+
182+static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
183+ struct v4l2_frmsizeenum *fsize)
184+{
185+ unsigned int i;
186+
187+ if (fsize->index > 0)
188+ return -EINVAL;
189+
190+ for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
191+ if (fsize->pixel_format != ipu6_isys_pfmts[i].pixelformat)
192+ continue;
193+
194+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
195+ fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;
196+ fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;
197+ fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;
198+ fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;
199+ fsize->stepwise.step_width = 2;
200+ fsize->stepwise.step_height = 2;
201+
202+ return 0;
203+ }
204+
205+ return -EINVAL;
206+}
207+
208+static int ipu6_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,
209+ struct v4l2_format *f)
210+{
211+ struct ipu6_isys_video *av = video_drvdata(file);
212+
213+ f->fmt.pix = av->pix_fmt;
214+
215+ return 0;
216+}
217+
218+static int ipu6_isys_vidioc_g_fmt_meta_cap(struct file *file, void *fh,
219+ struct v4l2_format *f)
220+{
221+ struct ipu6_isys_video *av = video_drvdata(file);
222+
223+ f->fmt.meta = av->meta_fmt;
224+
225+ return 0;
226+}
227+
228+static void ipu6_isys_try_fmt_cap(struct ipu6_isys_video *av, u32 type,
229+ u32 *format, u32 *width, u32 *height,
230+ u32 *bytesperline, u32 *sizeimage)
231+{
232+ const struct ipu6_isys_pixelformat *pfmt =
233+ ipu6_isys_get_isys_format(*format, type);
234+
235+ *format = pfmt->pixelformat;
236+ *width = clamp(*width, IPU6_ISYS_MIN_WIDTH, IPU6_ISYS_MAX_WIDTH);
237+ *height = clamp(*height, IPU6_ISYS_MIN_HEIGHT, IPU6_ISYS_MAX_HEIGHT);
238+
239+ if (pfmt->bpp != pfmt->bpp_packed)
240+ *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
241+ else
242+ *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);
243+
244+ *bytesperline = ALIGN(*bytesperline, av->isys->line_align);
245+
246+ /*
247+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit
248+ * is a power of two, and a line should be transferred as few units
249+ * as possible. The result is that up to line length more data than
250+ * the image size may be transferred to memory after the image.
251+ * Another limitation is the GDA allocation unit size. For low
252+ * resolution it gives a bigger number. Use larger one to avoid
253+ * memory corruption.
254+ */
255+ *sizeimage = *bytesperline * *height +
256+ max(*bytesperline,
257+ av->isys->pdata->ipdata->isys_dma_overshoot);
258+}
259+
260+static void __ipu6_isys_vidioc_try_fmt_vid_cap(struct ipu6_isys_video *av,
261+ struct v4l2_format *f)
262+{
263+ ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,
264+ &f->fmt.pix.width, &f->fmt.pix.height,
265+ &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);
266+
267+ f->fmt.pix.field = V4L2_FIELD_NONE;
268+ f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
269+ f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
270+ f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
271+ f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
272+}
273+
274+static int ipu6_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,
275+ struct v4l2_format *f)
276+{
277+ struct ipu6_isys_video *av = video_drvdata(file);
278+
279+ if (vb2_is_busy(&av->aq.vbq))
280+ return -EBUSY;
281+
282+ __ipu6_isys_vidioc_try_fmt_vid_cap(av, f);
283+
284+ return 0;
285+}
286+
287+static int __ipu6_isys_vidioc_try_fmt_meta_cap(struct ipu6_isys_video *av,
288+ struct v4l2_format *f)
289+{
290+ ipu6_isys_try_fmt_cap(av, f->type, &f->fmt.meta.dataformat,
291+ &f->fmt.meta.width, &f->fmt.meta.height,
292+ &f->fmt.meta.bytesperline,
293+ &f->fmt.meta.buffersize);
294+
295+ return 0;
296+}
297+
298+static int ipu6_isys_vidioc_try_fmt_meta_cap(struct file *file, void *fh,
299+ struct v4l2_format *f)
300+{
301+ struct ipu6_isys_video *av = video_drvdata(file);
302+
303+ __ipu6_isys_vidioc_try_fmt_meta_cap(av, f);
304+
305+ return 0;
306+}
307+
308+static int ipu6_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,
309+ struct v4l2_format *f)
310+{
311+ struct ipu6_isys_video *av = video_drvdata(file);
312+
313+ ipu6_isys_vidioc_try_fmt_vid_cap(file, fh, f);
314+ av->pix_fmt = f->fmt.pix;
315+
316+ return 0;
317+}
318+
319+static int ipu6_isys_vidioc_s_fmt_meta_cap(struct file *file, void *fh,
320+ struct v4l2_format *f)
321+{
322+ struct ipu6_isys_video *av = video_drvdata(file);
323+
324+ if (vb2_is_busy(&av->aq.vbq))
325+ return -EBUSY;
326+
327+ ipu6_isys_vidioc_try_fmt_meta_cap(file, fh, f);
328+ av->meta_fmt = f->fmt.meta;
329+
330+ return 0;
331+}
332+
333+static int ipu6_isys_vidioc_reqbufs(struct file *file, void *priv,
334+ struct v4l2_requestbuffers *p)
335+{
336+ struct ipu6_isys_video *av = video_drvdata(file);
337+ int ret;
338+
339+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
340+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
341+
342+ ret = vb2_queue_change_type(&av->aq.vbq, p->type);
343+ if (ret)
344+ return ret;
345+
346+ return vb2_ioctl_reqbufs(file, priv, p);
347+}
348+
349+static int ipu6_isys_vidioc_create_bufs(struct file *file, void *priv,
350+ struct v4l2_create_buffers *p)
351+{
352+ struct ipu6_isys_video *av = video_drvdata(file);
353+ int ret;
354+
355+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
356+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
357+
358+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
359+ if (ret)
360+ return ret;
361+
362+ return vb2_ioctl_create_bufs(file, priv, p);
363+}
364+
365+static int link_validate(struct media_link *link)
366+{
367+ struct ipu6_isys_video *av =
368+ container_of(link->sink, struct ipu6_isys_video, pad);
369+ struct device *dev = &av->isys->adev->auxdev.dev;
370+ struct v4l2_subdev_state *s_state;
371+ struct v4l2_subdev *s_sd;
372+ struct v4l2_mbus_framefmt *s_fmt;
373+ struct media_pad *s_pad;
374+ u32 s_stream, code;
375+ int ret = -EPIPE;
376+
377+ if (!link->source->entity)
378+ return ret;
379+
380+ s_sd = media_entity_to_v4l2_subdev(link->source->entity);
381+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
382+ if (!s_state)
383+ return ret;
384+
385+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
386+ link->source->entity->name, link->source->index,
387+ link->sink->entity->name);
388+
389+ s_pad = media_pad_remote_pad_first(&av->pad);
390+ s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
391+
392+ v4l2_subdev_lock_state(s_state);
393+
394+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
395+ if (!s_fmt) {
396+ dev_err(dev, "failed to get source pad format\n");
397+ goto unlock;
398+ }
399+
400+ code = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0)->code;
401+
402+ if (s_fmt->width != ipu6_isys_get_frame_width(av) ||
403+ s_fmt->height != ipu6_isys_get_frame_height(av) ||
404+ s_fmt->code != code) {
405+ dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
406+ s_fmt->width, s_fmt->height, s_fmt->code,
407+ ipu6_isys_get_frame_width(av),
408+ ipu6_isys_get_frame_height(av), code);
409+ goto unlock;
410+ }
411+
412+ v4l2_subdev_unlock_state(s_state);
413+
414+ return 0;
415+unlock:
416+ v4l2_subdev_unlock_state(s_state);
417+
418+ return ret;
419+}
420+
421+static void get_stream_opened(struct ipu6_isys_video *av)
422+{
423+ unsigned long flags;
424+
425+ spin_lock_irqsave(&av->isys->streams_lock, flags);
426+ av->isys->stream_opened++;
427+ spin_unlock_irqrestore(&av->isys->streams_lock, flags);
428+}
429+
430+static void put_stream_opened(struct ipu6_isys_video *av)
431+{
432+ unsigned long flags;
433+
434+ spin_lock_irqsave(&av->isys->streams_lock, flags);
435+ av->isys->stream_opened--;
436+ spin_unlock_irqrestore(&av->isys->streams_lock, flags);
437+}
438+
439+static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
440+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
441+{
442+ struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
443+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
444+ struct ipu6_fw_isys_input_pin_info_abi *input_pin;
445+ struct ipu6_fw_isys_output_pin_info_abi *output_pin;
446+ struct ipu6_isys_stream *stream = av->stream;
447+ struct ipu6_isys_queue *aq = &av->aq;
448+ struct v4l2_mbus_framefmt fmt;
449+ const struct ipu6_isys_pixelformat *pfmt =
450+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
451+ struct v4l2_rect v4l2_crop;
452+ struct ipu6_isys *isys = av->isys;
453+ struct device *dev = &isys->adev->auxdev.dev;
454+ int input_pins = cfg->nof_input_pins++;
455+ int output_pins;
456+ u32 src_stream;
457+ int ret;
458+
459+ src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
460+ ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
461+ &fmt);
462+ if (ret < 0) {
463+ dev_err(dev, "can't get stream format (%d)\n", ret);
464+ return ret;
465+ }
466+
467+ ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
468+ &v4l2_crop);
469+ if (ret < 0) {
470+ dev_err(dev, "can't get stream crop (%d)\n", ret);
471+ return ret;
472+ }
473+
474+ input_pin = &cfg->input_pins[input_pins];
475+ input_pin->input_res.width = fmt.width;
476+ input_pin->input_res.height = fmt.height;
477+ input_pin->dt = av->dt;
478+ input_pin->bits_per_pix = pfmt->bpp_packed;
479+ input_pin->mapped_dt = 0x40; /* invalid mipi data type */
480+ input_pin->mipi_decompression = 0;
481+ input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
482+ input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ?
483+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
484+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
485+ input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
486+
487+ output_pins = cfg->nof_output_pins++;
488+ aq->fw_output = output_pins;
489+ stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready;
490+ stream->output_pins[output_pins].aq = aq;
491+
492+ output_pin = &cfg->output_pins[output_pins];
493+ output_pin->input_pin_id = input_pins;
494+ output_pin->output_res.width = ipu6_isys_get_frame_width(av);
495+ output_pin->output_res.height = ipu6_isys_get_frame_height(av);
496+
497+ output_pin->stride = ipu6_isys_get_bytes_per_line(av);
498+ if (pfmt->bpp != pfmt->bpp_packed)
499+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
500+ else
501+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
502+ output_pin->ft = pfmt->css_pixelformat;
503+ output_pin->send_irq = 1;
504+ memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
505+ output_pin->s2m_pixel_soc_pixel_remapping =
506+ S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
507+ output_pin->csi_be_soc_pixel_remapping =
508+ CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
509+
510+ output_pin->snoopable = true;
511+ output_pin->error_handling_enable = false;
512+ output_pin->sensor_type = isys->sensor_type++;
513+ if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
514+ isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
515+
516+ return 0;
517+}
518+
519+static int start_stream_firmware(struct ipu6_isys_video *av,
520+ struct ipu6_isys_buffer_list *bl)
521+{
522+ struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;
523+ struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
524+ struct ipu6_isys_stream *stream = av->stream;
525+ struct device *dev = &av->isys->adev->auxdev.dev;
526+ struct isys_fw_msgs *msg = NULL;
527+ struct ipu6_isys_queue *aq;
528+ int ret, retout, tout;
529+ u16 send_type;
530+
531+ msg = ipu6_get_fw_msg_buf(stream);
532+ if (!msg)
533+ return -ENOMEM;
534+
535+ stream_cfg = &msg->fw_msg.stream;
536+ stream_cfg->src = stream->stream_source;
537+ stream_cfg->vc = stream->vc;
538+ stream_cfg->isl_use = 0;
539+ stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
540+
541+ list_for_each_entry(aq, &stream->queues, node) {
542+ struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
543+
544+ ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);
545+ if (ret < 0) {
546+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
547+ return ret;
548+ }
549+ }
550+
551+ ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg);
552+
553+ stream->nr_output_pins = stream_cfg->nof_output_pins;
554+
555+ reinit_completion(&stream->stream_open_completion);
556+
557+ ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
558+ stream_cfg, msg->dma_addr,
559+ sizeof(*stream_cfg),
560+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
561+ if (ret < 0) {
562+ dev_err(dev, "can't open stream (%d)\n", ret);
563+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
564+ return ret;
565+ }
566+
567+ get_stream_opened(av);
568+
569+ tout = wait_for_completion_timeout(&stream->stream_open_completion,
570+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
571+
572+ ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
573+
574+ if (!tout) {
575+ dev_err(dev, "stream open time out\n");
576+ ret = -ETIMEDOUT;
577+ goto out_put_stream_opened;
578+ }
579+ if (stream->error) {
580+ dev_err(dev, "stream open error: %d\n", stream->error);
581+ ret = -EIO;
582+ goto out_put_stream_opened;
583+ }
584+ dev_dbg(dev, "start stream: open complete\n");
585+
586+ if (bl) {
587+ msg = ipu6_get_fw_msg_buf(stream);
588+ if (!msg) {
589+ ret = -ENOMEM;
590+ goto out_put_stream_opened;
591+ }
592+ buf = &msg->fw_msg.frame;
593+ ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
594+ ipu6_isys_buffer_list_queue(bl,
595+ IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
596+ }
597+
598+ reinit_completion(&stream->stream_start_completion);
599+
600+ if (bl) {
601+ send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
602+ ipu6_fw_isys_dump_frame_buff_set(dev, buf,
603+ stream_cfg->nof_output_pins);
604+ ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
605+ buf, msg->dma_addr,
606+ sizeof(*buf), send_type);
607+ } else {
608+ send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
609+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
610+ send_type);
611+ }
612+
613+ if (ret < 0) {
614+ dev_err(dev, "can't start streaming (%d)\n", ret);
615+ goto out_stream_close;
616+ }
617+
618+ tout = wait_for_completion_timeout(&stream->stream_start_completion,
619+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
620+ if (!tout) {
621+ dev_err(dev, "stream start time out\n");
622+ ret = -ETIMEDOUT;
623+ goto out_stream_close;
624+ }
625+ if (stream->error) {
626+ dev_err(dev, "stream start error: %d\n", stream->error);
627+ ret = -EIO;
628+ goto out_stream_close;
629+ }
630+ dev_dbg(dev, "start stream: complete\n");
631+
632+ return 0;
633+
634+out_stream_close:
635+ reinit_completion(&stream->stream_close_completion);
636+
637+ retout = ipu6_fw_isys_simple_cmd(av->isys,
638+ stream->stream_handle,
639+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
640+ if (retout < 0) {
641+ dev_dbg(dev, "can't close stream (%d)\n", retout);
642+ goto out_put_stream_opened;
643+ }
644+
645+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
646+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
647+ if (!tout)
648+ dev_err(dev, "stream close time out\n");
649+ else if (stream->error)
650+ dev_err(dev, "stream close error: %d\n", stream->error);
651+ else
652+ dev_dbg(dev, "stream close complete\n");
653+
654+out_put_stream_opened:
655+ put_stream_opened(av);
656+
657+ return ret;
658+}
659+
660+static void stop_streaming_firmware(struct ipu6_isys_video *av)
661+{
662+ struct device *dev = &av->isys->adev->auxdev.dev;
663+ struct ipu6_isys_stream *stream = av->stream;
664+ int ret, tout;
665+
666+ reinit_completion(&stream->stream_stop_completion);
667+
668+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
669+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
670+
671+ if (ret < 0) {
672+ dev_err(dev, "can't stop stream (%d)\n", ret);
673+ return;
674+ }
675+
676+ tout = wait_for_completion_timeout(&stream->stream_stop_completion,
677+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
678+ if (!tout)
679+ dev_warn(dev, "stream stop time out\n");
680+ else if (stream->error)
681+ dev_warn(dev, "stream stop error: %d\n", stream->error);
682+ else
683+ dev_dbg(dev, "stop stream: complete\n");
684+}
685+
686+static void close_streaming_firmware(struct ipu6_isys_video *av)
687+{
688+ struct ipu6_isys_stream *stream = av->stream;
689+ struct device *dev = &av->isys->adev->auxdev.dev;
690+ int ret, tout;
691+
692+ reinit_completion(&stream->stream_close_completion);
693+
694+ ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
695+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
696+ if (ret < 0) {
697+ dev_err(dev, "can't close stream (%d)\n", ret);
698+ return;
699+ }
700+
701+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
702+ IPU6_FW_CALL_TIMEOUT_JIFFIES);
703+ if (!tout)
704+ dev_warn(dev, "stream close time out\n");
705+ else if (stream->error)
706+ dev_warn(dev, "stream close error: %d\n", stream->error);
707+ else
708+ dev_dbg(dev, "close stream: complete\n");
709+
710+ put_stream_opened(av);
711+}
712+
713+int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
714+ struct media_entity *source_entity,
715+ int nr_queues)
716+{
717+ struct ipu6_isys_stream *stream = av->stream;
718+ struct ipu6_isys_csi2 *csi2;
719+
720+ if (WARN_ON(stream->nr_streaming))
721+ return -EINVAL;
722+
723+ stream->nr_queues = nr_queues;
724+ atomic_set(&stream->sequence, 0);
725+
726+ stream->seq_index = 0;
727+ memset(stream->seq, 0, sizeof(stream->seq));
728+
729+ if (WARN_ON(!list_empty(&stream->queues)))
730+ return -EINVAL;
731+
732+ stream->stream_source = stream->asd->source;
733+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
734+ csi2->receiver_errors = 0;
735+ stream->source_entity = source_entity;
736+
737+ dev_dbg(&av->isys->adev->auxdev.dev,
738+ "prepare stream: external entity %s\n",
739+ stream->source_entity->name);
740+
741+ return 0;
742+}
743+
744+void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
745+ bool state)
746+{
747+ struct ipu6_isys *isys = av->isys;
748+ struct ipu6_isys_csi2 *csi2 = NULL;
749+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
750+ struct device *dev = &isys->adev->auxdev.dev;
751+ struct v4l2_mbus_framefmt format;
752+ struct v4l2_subdev *esd;
753+ struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
754+ unsigned int bpp, lanes;
755+ s64 link_freq = 0;
756+ u64 pixel_rate = 0;
757+ int ret;
758+
759+ if (!state)
760+ return;
761+
762+ esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
763+
764+ av->watermark.width = ipu6_isys_get_frame_width(av);
765+ av->watermark.height = ipu6_isys_get_frame_height(av);
766+ av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
767+ av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
768+
769+ ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);
770+ if (!ret && hb.value >= 0)
771+ av->watermark.hblank = hb.value;
772+ else
773+ av->watermark.hblank = 0;
774+
775+ csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
776+ link_freq = ipu6_isys_csi2_get_link_freq(csi2);
777+ if (link_freq > 0) {
778+ lanes = csi2->nlanes;
779+ ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
780+ av->source_stream, &format);
781+ if (!ret) {
782+ bpp = ipu6_isys_mbus_code_to_bpp(format.code);
783+ pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
784+ }
785+ }
786+
787+ av->watermark.pixel_rate = pixel_rate;
788+
789+ if (!pixel_rate) {
790+ mutex_lock(&iwake_watermark->mutex);
791+ iwake_watermark->force_iwake_disable = true;
792+ mutex_unlock(&iwake_watermark->mutex);
793+ dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
794+ av->stream->source_entity->name);
795+ }
796+}
797+
798+static void calculate_stream_datarate(struct ipu6_isys_video *av)
799+{
800+ struct video_stream_watermark *watermark = &av->watermark;
801+ const struct ipu6_isys_pixelformat *pfmt =
802+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
803+ u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;
804+ u64 line_time_ns, stream_data_rate;
805+ u16 shift, size;
806+
807+ shift = watermark->sram_gran_shift;
808+ size = watermark->sram_gran_size;
809+
810+ pixels_per_line = watermark->width + watermark->hblank;
811+ line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC,
812+ watermark->pixel_rate);
813+ bytes_per_line = watermark->width * pfmt->bpp / 8;
814+ pages_per_line = DIV_ROUND_UP(bytes_per_line, size);
815+ pb_bytes_per_line = pages_per_line << shift;
816+ stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);
817+
818+ watermark->stream_data_rate = stream_data_rate;
819+}
820+
821+void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)
822+{
823+ struct isys_iwake_watermark *iwake_watermark =
824+ &av->isys->iwake_watermark;
825+
826+ if (!av->watermark.pixel_rate)
827+ return;
828+
829+ if (state) {
830+ calculate_stream_datarate(av);
831+ mutex_lock(&iwake_watermark->mutex);
832+ list_add(&av->watermark.stream_node,
833+ &iwake_watermark->video_list);
834+ mutex_unlock(&iwake_watermark->mutex);
835+ } else {
836+ av->watermark.stream_data_rate = 0;
837+ mutex_lock(&iwake_watermark->mutex);
838+ list_del(&av->watermark.stream_node);
839+ mutex_unlock(&iwake_watermark->mutex);
840+ }
841+
842+ update_watermark_setting(av->isys);
843+}
844+
845+void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
846+{
847+ struct device *dev;
848+ unsigned int i;
849+ unsigned long flags;
850+
851+ if (!stream) {
852+ pr_err("ipu6-isys: no available stream\n");
853+ return;
854+ }
855+
856+ dev = &stream->isys->adev->auxdev.dev;
857+
858+ spin_lock_irqsave(&stream->isys->streams_lock, flags);
859+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
860+ if (&stream->isys->streams[i] == stream) {
861+ if (stream->isys->streams_ref_count[i] > 0)
862+ stream->isys->streams_ref_count[i]--;
863+ else
864+ dev_warn(dev, "invalid stream %d\n", i);
865+
866+ break;
867+ }
868+ }
869+ spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
870+}
871+
872+static struct ipu6_isys_stream *
873+ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)
874+{
875+ struct ipu6_isys_stream *stream = NULL;
876+ struct ipu6_isys *isys = av->isys;
877+ unsigned long flags;
878+ unsigned int i;
879+ u8 vc = av->vc;
880+
881+ if (!isys)
882+ return NULL;
883+
884+ spin_lock_irqsave(&isys->streams_lock, flags);
885+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
886+ if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
887+ isys->streams[i].asd == asd) {
888+ isys->streams_ref_count[i]++;
889+ stream = &isys->streams[i];
890+ break;
891+ }
892+ }
893+
894+ if (!stream) {
895+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
896+ if (!isys->streams_ref_count[i]) {
897+ isys->streams_ref_count[i]++;
898+ stream = &isys->streams[i];
899+ stream->vc = vc;
900+ stream->asd = asd;
901+ break;
902+ }
903+ }
904+ }
905+ spin_unlock_irqrestore(&isys->streams_lock, flags);
906+
907+ return stream;
908+}
909+
910+struct ipu6_isys_stream *
911+ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)
912+{
913+ unsigned long flags;
914+ struct ipu6_isys_stream *stream = NULL;
915+
916+ if (!isys)
917+ return NULL;
918+
919+ if (stream_handle >= IPU6_ISYS_MAX_STREAMS) {
920+ dev_err(&isys->adev->auxdev.dev,
921+ "stream_handle %d is invalid\n", stream_handle);
922+ return NULL;
923+ }
924+
925+ spin_lock_irqsave(&isys->streams_lock, flags);
926+ if (isys->streams_ref_count[stream_handle] > 0) {
927+ isys->streams_ref_count[stream_handle]++;
928+ stream = &isys->streams[stream_handle];
929+ }
930+ spin_unlock_irqrestore(&isys->streams_lock, flags);
931+
932+ return stream;
933+}
934+
935+struct ipu6_isys_stream *
936+ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)
937+{
938+ struct ipu6_isys_stream *stream = NULL;
939+ unsigned long flags;
940+ unsigned int i;
941+
942+ if (!isys)
943+ return NULL;
944+
945+ if (source < 0) {
946+ dev_err(&stream->isys->adev->auxdev.dev,
947+ "query stream with invalid port number\n");
948+ return NULL;
949+ }
950+
951+ spin_lock_irqsave(&isys->streams_lock, flags);
952+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
953+ if (!isys->streams_ref_count[i])
954+ continue;
955+
956+ if (isys->streams[i].stream_source == source &&
957+ isys->streams[i].vc == vc) {
958+ stream = &isys->streams[i];
959+ isys->streams_ref_count[i]++;
960+ break;
961+ }
962+ }
963+ spin_unlock_irqrestore(&isys->streams_lock, flags);
964+
965+ return stream;
966+}
967+
968+static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av)
969+{
970+ struct media_pipeline *pipeline =
971+ media_entity_pipeline(&__av->vdev.entity);
972+ unsigned int i;
973+ u64 stream_mask = 0;
974+
975+ for (i = 0; i < NR_OF_CSI2_SRC_PADS; i++) {
976+ struct ipu6_isys_video *av = &__av->csi2->av[i];
977+
978+ if (pipeline == media_entity_pipeline(&av->vdev.entity))
979+ stream_mask |= BIT_ULL(av->source_stream);
980+ }
981+
982+ return stream_mask;
983+}
984+
985+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
986+ struct ipu6_isys_buffer_list *bl)
987+{
988+ struct v4l2_subdev_krouting *routing;
989+ struct ipu6_isys_stream *stream = av->stream;
990+ struct v4l2_subdev_state *subdev_state;
991+ struct device *dev = &av->isys->adev->auxdev.dev;
992+ struct v4l2_subdev *sd;
993+ struct v4l2_subdev *ssd;
994+ struct media_pad *r_pad;
995+ struct media_pad *s_pad;
996+ u32 sink_pad, sink_stream;
997+ u64 r_stream;
998+ u64 stream_mask = 0;
999+ int ret = 0;
1000+
1001+ dev_dbg(dev, "set stream: %d\n", state);
1002+
1003+ if (WARN(!stream->source_entity, "No source entity for stream\n"))
1004+ return -ENODEV;
1005+
1006+ ssd = media_entity_to_v4l2_subdev(stream->source_entity);
1007+ sd = &stream->asd->sd;
1008+ r_pad = media_pad_remote_pad_first(&av->pad);
1009+ r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
1010+
1011+ subdev_state = v4l2_subdev_lock_and_get_active_state(sd);
1012+ routing = &subdev_state->routing;
1013+ ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,
1014+ r_stream, &sink_pad,
1015+ &sink_stream);
1016+ v4l2_subdev_unlock_state(subdev_state);
1017+ if (ret)
1018+ return ret;
1019+
1020+ s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]);
1021+
1022+ stream_mask = get_stream_mask_by_pipeline(av);
1023+ if (!state) {
1024+ stop_streaming_firmware(av);
1025+
1026+ /* stop external sub-device now. */
1027+ dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask,
1028+ ssd->name);
1029+ ret = v4l2_subdev_disable_streams(ssd, s_pad->index,
1030+ stream_mask);
1031+ if (ret) {
1032+ dev_err(dev, "disable streams of %s failed with %d\n",
1033+ ssd->name, ret);
1034+ return ret;
1035+ }
1036+
1037+ /* stop sub-device which connects with video */
1038+ dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name,
1039+ r_pad->index);
1040+ ret = v4l2_subdev_call(sd, video, s_stream, state);
1041+ if (ret) {
1042+ dev_err(dev, "stream off %s failed with %d\n", sd->name,
1043+ ret);
1044+ return ret;
1045+ }
1046+ close_streaming_firmware(av);
1047+ } else {
1048+ ret = start_stream_firmware(av, bl);
1049+ if (ret) {
1050+ dev_err(dev, "start stream of firmware failed\n");
1051+ return ret;
1052+ }
1053+
1054+ /* start sub-device which connects with video */
1055+ dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index);
1056+ ret = v4l2_subdev_call(sd, video, s_stream, state);
1057+ if (ret) {
1058+ dev_err(dev, "stream on %s failed with %d\n", sd->name,
1059+ ret);
1060+ goto out_media_entity_stop_streaming_firmware;
1061+ }
1062+
1063+ /* start external sub-device now. */
1064+ dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask,
1065+ ssd->name);
1066+ ret = v4l2_subdev_enable_streams(ssd, s_pad->index,
1067+ stream_mask);
1068+ if (ret) {
1069+ dev_err(dev,
1070+ "enable streams 0x%llx of %s failed with %d\n",
1071+ stream_mask, stream->source_entity->name, ret);
1072+ goto out_media_entity_stop_streaming;
1073+ }
1074+ }
1075+
1076+ av->streaming = state;
1077+
1078+ return 0;
1079+
1080+out_media_entity_stop_streaming:
1081+ v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream));
1082+
1083+out_media_entity_stop_streaming_firmware:
1084+ stop_streaming_firmware(av);
1085+
1086+ return ret;
1087+}
1088+
1089+static const struct v4l2_ioctl_ops ipu6_v4l2_ioctl_ops = {
1090+ .vidioc_querycap = ipu6_isys_vidioc_querycap,
1091+ .vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
1092+ .vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,
1093+ .vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
1094+ .vidioc_g_fmt_vid_cap = ipu6_isys_vidioc_g_fmt_vid_cap,
1095+ .vidioc_s_fmt_vid_cap = ipu6_isys_vidioc_s_fmt_vid_cap,
1096+ .vidioc_try_fmt_vid_cap = ipu6_isys_vidioc_try_fmt_vid_cap,
1097+ .vidioc_g_fmt_meta_cap = ipu6_isys_vidioc_g_fmt_meta_cap,
1098+ .vidioc_s_fmt_meta_cap = ipu6_isys_vidioc_s_fmt_meta_cap,
1099+ .vidioc_try_fmt_meta_cap = ipu6_isys_vidioc_try_fmt_meta_cap,
1100+ .vidioc_reqbufs = ipu6_isys_vidioc_reqbufs,
1101+ .vidioc_create_bufs = ipu6_isys_vidioc_create_bufs,
1102+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
1103+ .vidioc_querybuf = vb2_ioctl_querybuf,
1104+ .vidioc_qbuf = vb2_ioctl_qbuf,
1105+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
1106+ .vidioc_streamon = vb2_ioctl_streamon,
1107+ .vidioc_streamoff = vb2_ioctl_streamoff,
1108+ .vidioc_expbuf = vb2_ioctl_expbuf,
1109+};
1110+
1111+static const struct media_entity_operations entity_ops = {
1112+ .link_validate = link_validate,
1113+};
1114+
1115+static const struct v4l2_file_operations isys_fops = {
1116+ .owner = THIS_MODULE,
1117+ .poll = vb2_fop_poll,
1118+ .unlocked_ioctl = video_ioctl2,
1119+ .mmap = vb2_fop_mmap,
1120+ .open = video_open,
1121+ .release = vb2_fop_release,
1122+};
1123+
1124+int ipu6_isys_fw_open(struct ipu6_isys *isys)
1125+{
1126+ struct ipu6_bus_device *adev = isys->adev;
1127+ const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;
1128+ int ret;
1129+
1130+ ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
1131+ if (ret < 0)
1132+ return ret;
1133+
1134+ mutex_lock(&isys->mutex);
1135+
1136+ if (isys->ref_count++)
1137+ goto unlock;
1138+
1139+ ipu6_configure_spc(adev->isp, &ipdata->hw_variant,
1140+ IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,
1141+ adev->pkg_dir, adev->pkg_dir_dma_addr);
1142+
1143+ /*
1144+ * Buffers could have been left to wrong queue at last closure.
1145+ * Move them now back to empty buffer queue.
1146+ */
1147+ ipu6_cleanup_fw_msg_bufs(isys);
1148+
1149+ if (isys->fwcom) {
1150+ /*
1151+ * Something went wrong in previous shutdown. As we are now
1152+ * restarting isys we can safely delete old context.
1153+ */
1154+ dev_warn(&adev->auxdev.dev, "clearing old context\n");
1155+ ipu6_fw_isys_cleanup(isys);
1156+ }
1157+
1158+ ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams);
1159+ if (ret < 0)
1160+ goto out;
1161+
1162+unlock:
1163+ mutex_unlock(&isys->mutex);
1164+
1165+ return 0;
1166+
1167+out:
1168+ isys->ref_count--;
1169+ mutex_unlock(&isys->mutex);
1170+ pm_runtime_put(&adev->auxdev.dev);
1171+
1172+ return ret;
1173+}
1174+
1175+void ipu6_isys_fw_close(struct ipu6_isys *isys)
1176+{
1177+ mutex_lock(&isys->mutex);
1178+
1179+ isys->ref_count--;
1180+ if (!isys->ref_count) {
1181+ ipu6_fw_isys_close(isys);
1182+ if (isys->fwcom) {
1183+ isys->need_reset = true;
1184+ dev_warn(&isys->adev->auxdev.dev,
1185+ "failed to close fw isys\n");
1186+ }
1187+ }
1188+
1189+ mutex_unlock(&isys->mutex);
1190+
1191+ if (isys->need_reset)
1192+ pm_runtime_put_sync(&isys->adev->auxdev.dev);
1193+ else
1194+ pm_runtime_put(&isys->adev->auxdev.dev);
1195+}
1196+
1197+int ipu6_isys_setup_video(struct ipu6_isys_video *av,
1198+ struct media_entity **source_entity, int *nr_queues)
1199+{
1200+ const struct ipu6_isys_pixelformat *pfmt =
1201+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
1202+ struct device *dev = &av->isys->adev->auxdev.dev;
1203+ struct v4l2_mbus_frame_desc_entry entry;
1204+ struct v4l2_subdev_route *route = NULL;
1205+ struct v4l2_subdev_route *r;
1206+ struct v4l2_subdev_state *state;
1207+ struct ipu6_isys_subdev *asd;
1208+ struct v4l2_subdev *remote_sd;
1209+ struct media_pipeline *pipeline;
1210+ struct media_pad *source_pad, *remote_pad;
1211+ int ret = -EINVAL;
1212+
1213+ *nr_queues = 0;
1214+
1215+ remote_pad = media_pad_remote_pad_unique(&av->pad);
1216+ if (IS_ERR(remote_pad)) {
1217+ dev_dbg(dev, "failed to get remote pad\n");
1218+ return PTR_ERR(remote_pad);
1219+ }
1220+
1221+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
1222+ asd = to_ipu6_isys_subdev(remote_sd);
1223+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
1224+ if (!source_pad) {
1225+ dev_dbg(dev, "No external source entity\n");
1226+ return -ENODEV;
1227+ }
1228+
1229+ *source_entity = source_pad->entity;
1230+
1231+ /* Find the root */
1232+ state = v4l2_subdev_lock_and_get_active_state(remote_sd);
1233+ for_each_active_route(&state->routing, r) {
1234+ (*nr_queues)++;
1235+
1236+ if (r->source_pad == remote_pad->index)
1237+ route = r;
1238+ }
1239+
1240+ if (!route) {
1241+ v4l2_subdev_unlock_state(state);
1242+ dev_dbg(dev, "Failed to find route\n");
1243+ return -ENODEV;
1244+ }
1245+ av->source_stream = route->sink_stream;
1246+ v4l2_subdev_unlock_state(state);
1247+
1248+ ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
1249+ to_ipu6_isys_csi2(asd),
1250+ *source_entity, &entry);
1251+ if (ret == -ENOIOCTLCMD) {
1252+ av->vc = 0;
1253+ av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code);
1254+ } else if (!ret) {
1255+ dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
1256+ entry.stream, entry.length, entry.bus.csi2.vc,
1257+ entry.bus.csi2.dt);
1258+
1259+ av->vc = entry.bus.csi2.vc;
1260+ av->dt = entry.bus.csi2.dt;
1261+ } else {
1262+ dev_err(dev, "failed to get remote frame desc\n");
1263+ return ret;
1264+ }
1265+
1266+ pipeline = media_entity_pipeline(&av->vdev.entity);
1267+ if (!pipeline)
1268+ ret = video_device_pipeline_alloc_start(&av->vdev);
1269+ else
1270+ ret = video_device_pipeline_start(&av->vdev, pipeline);
1271+ if (ret < 0) {
1272+ dev_dbg(dev, "media pipeline start failed\n");
1273+ return ret;
1274+ }
1275+
1276+ av->stream = ipu6_isys_get_stream(av, asd);
1277+ if (!av->stream) {
1278+ video_device_pipeline_stop(&av->vdev);
1279+ dev_err(dev, "no available stream for firmware\n");
1280+ return -EINVAL;
1281+ }
1282+
1283+ return 0;
1284+}
1285+
1286+/*
1287+ * Do everything that's needed to initialise things related to video
1288+ * buffer queue, video node, and the related media entity. The caller
1289+ * is expected to assign isys field and set the name of the video
1290+ * device.
1291+ */
1292+int ipu6_isys_video_init(struct ipu6_isys_video *av)
1293+{
1294+ struct v4l2_format format = {
1295+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
1296+ .fmt.pix = {
1297+ .width = 1920,
1298+ .height = 1080,
1299+ },
1300+ };
1301+ struct v4l2_format format_meta = {
1302+ .type = V4L2_BUF_TYPE_META_CAPTURE,
1303+ .fmt.meta = {
1304+ .width = 1920,
1305+ .height = 4,
1306+ },
1307+ };
1308+ int ret;
1309+
1310+ mutex_init(&av->mutex);
1311+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
1312+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
1313+ av->vdev.vfl_dir = VFL_DIR_RX;
1314+
1315+ ret = ipu6_isys_queue_init(&av->aq);
1316+ if (ret)
1317+ goto out_free_watermark;
1318+
1319+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
1320+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
1321+ if (ret)
1322+ goto out_vb2_queue_release;
1323+
1324+ av->vdev.entity.ops = &entity_ops;
1325+ av->vdev.release = video_device_release_empty;
1326+ av->vdev.fops = &isys_fops;
1327+ av->vdev.v4l2_dev = &av->isys->v4l2_dev;
1328+ if (!av->vdev.ioctl_ops)
1329+ av->vdev.ioctl_ops = &ipu6_v4l2_ioctl_ops;
1330+ av->vdev.queue = &av->aq.vbq;
1331+ av->vdev.lock = &av->mutex;
1332+
1333+ __ipu6_isys_vidioc_try_fmt_vid_cap(av, &format);
1334+ av->pix_fmt = format.fmt.pix;
1335+ __ipu6_isys_vidioc_try_fmt_meta_cap(av, &format_meta);
1336+ av->meta_fmt = format_meta.fmt.meta;
1337+
1338+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
1339+ video_set_drvdata(&av->vdev, av);
1340+
1341+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
1342+ if (ret)
1343+ goto out_media_entity_cleanup;
1344+
1345+ return ret;
1346+
1347+out_media_entity_cleanup:
1348+ vb2_video_unregister_device(&av->vdev);
1349+ media_entity_cleanup(&av->vdev.entity);
1350+
1351+out_vb2_queue_release:
1352+ vb2_queue_release(&av->aq.vbq);
1353+
1354+out_free_watermark:
1355+ mutex_destroy(&av->mutex);
1356+
1357+ return ret;
1358+}
1359+
1360+void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
1361+{
1362+ vb2_video_unregister_device(&av->vdev);
1363+ media_entity_cleanup(&av->vdev.entity);
1364+ mutex_destroy(&av->mutex);
1365+}
1366+
1367+u32 ipu6_isys_get_format(struct ipu6_isys_video *av)
1368+{
1369+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1370+ return av->pix_fmt.pixelformat;
1371+
1372+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1373+ return av->meta_fmt.dataformat;
1374+
1375+ return 0;
1376+}
1377+
1378+u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av)
1379+{
1380+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1381+ return av->pix_fmt.sizeimage;
1382+
1383+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1384+ return av->meta_fmt.buffersize;
1385+
1386+ return 0;
1387+}
1388+
1389+u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av)
1390+{
1391+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1392+ return av->pix_fmt.bytesperline;
1393+
1394+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1395+ return av->meta_fmt.bytesperline;
1396+
1397+ return 0;
1398+}
1399+
1400+u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av)
1401+{
1402+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1403+ return av->pix_fmt.width;
1404+
1405+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1406+ return av->meta_fmt.width;
1407+
1408+ return 0;
1409+}
1410+
1411+u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av)
1412+{
1413+ if (av->aq.vbq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
1414+ return av->pix_fmt.height;
1415+
1416+ if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE)
1417+ return av->meta_fmt.height;
1418+
1419+ return 0;
1420+}
drivers/media/pci/intel/ipu6/ipu6-isys-video.h+141−0
@@ -0,0 +1,141 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_ISYS_VIDEO_H
5+#define IPU6_ISYS_VIDEO_H
6+
7+#include <linux/atomic.h>
8+#include <linux/completion.h>
9+#include <linux/container_of.h>
10+#include <linux/list.h>
11+#include <linux/mutex.h>
12+
13+#include <media/media-entity.h>
14+#include <media/v4l2-dev.h>
15+
16+#include "ipu6-isys-queue.h"
17+
18+#define IPU6_ISYS_OUTPUT_PINS 11
19+#define IPU6_ISYS_MAX_PARALLEL_SOF 2
20+
21+struct file;
22+struct ipu6_isys;
23+struct ipu6_isys_csi2;
24+struct ipu6_isys_subdev;
25+
26+struct ipu6_isys_pixelformat {
27+ u32 pixelformat;
28+ u32 bpp;
29+ u32 bpp_packed;
30+ u32 code;
31+ u32 css_pixelformat;
32+ bool is_meta;
33+};
34+
35+struct sequence_info {
36+ unsigned int sequence;
37+ u64 timestamp;
38+};
39+
40+struct output_pin_data {
41+ void (*pin_ready)(struct ipu6_isys_stream *stream,
42+ struct ipu6_fw_isys_resp_info_abi *info);
43+ struct ipu6_isys_queue *aq;
44+};
45+
46+/*
47+ * Align with firmware stream. Each stream represents a CSI virtual channel.
48+ * May map to multiple video devices
49+ */
50+struct ipu6_isys_stream {
51+ struct mutex mutex;
52+ struct media_entity *source_entity;
53+ atomic_t sequence;
54+ unsigned int seq_index;
55+ struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF];
56+ int stream_source;
57+ int stream_handle;
58+ unsigned int nr_output_pins;
59+ struct ipu6_isys_subdev *asd;
60+
61+ int nr_queues; /* Number of capture queues */
62+ int nr_streaming;
63+ int streaming; /* Has streaming been really started? */
64+ struct list_head queues;
65+ struct completion stream_open_completion;
66+ struct completion stream_close_completion;
67+ struct completion stream_start_completion;
68+ struct completion stream_stop_completion;
69+ struct ipu6_isys *isys;
70+
71+ struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS];
72+ int error;
73+ u8 vc;
74+};
75+
76+struct video_stream_watermark {
77+ u32 width;
78+ u32 height;
79+ u32 hblank;
80+ u32 frame_rate;
81+ u64 pixel_rate;
82+ u64 stream_data_rate;
83+ u16 sram_gran_shift;
84+ u16 sram_gran_size;
85+ struct list_head stream_node;
86+};
87+
88+struct ipu6_isys_video {
89+ struct ipu6_isys_queue aq;
90+ /* Serialise access to other fields in the struct. */
91+ struct mutex mutex;
92+ struct media_pad pad;
93+ struct video_device vdev;
94+ struct v4l2_pix_format pix_fmt;
95+ struct v4l2_meta_format meta_fmt;
96+ struct ipu6_isys *isys;
97+ struct ipu6_isys_csi2 *csi2;
98+ struct ipu6_isys_stream *stream;
99+ unsigned int streaming;
100+ struct video_stream_watermark watermark;
101+ u32 source_stream;
102+ u8 vc;
103+ u8 dt;
104+};
105+
106+#define ipu6_isys_queue_to_video(__aq) \
107+ container_of(__aq, struct ipu6_isys_video, aq)
108+
109+extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[];
110+extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[];
111+
112+const struct ipu6_isys_pixelformat *
113+ipu6_isys_get_isys_format(u32 pixelformat, u32 code);
114+int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
115+ struct media_entity *source_entity,
116+ int nr_queues);
117+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
118+ struct ipu6_isys_buffer_list *bl);
119+int ipu6_isys_fw_open(struct ipu6_isys *isys);
120+void ipu6_isys_fw_close(struct ipu6_isys *isys);
121+int ipu6_isys_setup_video(struct ipu6_isys_video *av,
122+ struct media_entity **source_entity, int *nr_queues);
123+int ipu6_isys_video_init(struct ipu6_isys_video *av);
124+void ipu6_isys_video_cleanup(struct ipu6_isys_video *av);
125+void ipu6_isys_put_stream(struct ipu6_isys_stream *stream);
126+struct ipu6_isys_stream *
127+ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle);
128+struct ipu6_isys_stream *
129+ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc);
130+
131+void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
132+ bool state);
133+void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
134+
135+u32 ipu6_isys_get_format(struct ipu6_isys_video *av);
136+u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av);
137+u32 ipu6_isys_get_bytes_per_line(struct ipu6_isys_video *av);
138+u32 ipu6_isys_get_frame_width(struct ipu6_isys_video *av);
139+u32 ipu6_isys_get_frame_height(struct ipu6_isys_video *av);
140+
141+#endif /* IPU6_ISYS_VIDEO_H */
drivers/media/pci/intel/ipu6/ipu6-isys.c+1367−0
@@ -0,0 +1,1367 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/auxiliary_bus.h>
7+#include <linux/bitfield.h>
8+#include <linux/bits.h>
9+#include <linux/completion.h>
10+#include <linux/container_of.h>
11+#include <linux/delay.h>
12+#include <linux/device.h>
13+#include <linux/dma-mapping.h>
14+#include <linux/err.h>
15+#include <linux/firmware.h>
16+#include <linux/io.h>
17+#include <linux/irqreturn.h>
18+#include <linux/list.h>
19+#include <linux/module.h>
20+#include <linux/mutex.h>
21+#include <linux/pci.h>
22+#include <linux/pm_runtime.h>
23+#include <linux/pm_qos.h>
24+#include <linux/slab.h>
25+#include <linux/spinlock.h>
26+#include <linux/string.h>
27+
28+#include <media/ipu-bridge.h>
29+#include <media/media-device.h>
30+#include <media/media-entity.h>
31+#include <media/v4l2-async.h>
32+#include <media/v4l2-device.h>
33+#include <media/v4l2-fwnode.h>
34+
35+#include "ipu6-bus.h"
36+#include "ipu6-cpd.h"
37+#include "ipu6-isys.h"
38+#include "ipu6-isys-csi2.h"
39+#include "ipu6-mmu.h"
40+#include "ipu6-platform-buttress-regs.h"
41+#include "ipu6-platform-isys-csi2-reg.h"
42+#include "ipu6-platform-regs.h"
43+
44+#define IPU6_BUTTRESS_FABIC_CONTROL 0x68
45+#define GDA_ENABLE_IWAKE_INDEX 2
46+#define GDA_IWAKE_THRESHOLD_INDEX 1
47+#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX 0
48+#define GDA_MEMOPEN_THRESHOLD_INDEX 3
49+#define DEFAULT_DID_RATIO 90
50+#define DEFAULT_IWAKE_THRESHOLD 0x42
51+#define DEFAULT_MEM_OPEN_TIME 10
52+#define ONE_THOUSAND_MICROSECOND 1000
53+/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */
54+#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE 0x800
55+
56+/* LTR & DID value are 10 bit at most */
57+#define LTR_DID_VAL_MAX 1023
58+#define LTR_DEFAULT_VALUE 0x70503c19
59+#define FILL_TIME_DEFAULT_VALUE 0xfff0783c
60+#define LTR_DID_PKGC_2R 20
61+#define LTR_SCALE_DEFAULT 5
62+#define LTR_SCALE_1024NS 2
63+#define DID_SCALE_1US 2
64+#define DID_SCALE_32US 3
65+#define REG_PKGC_PMON_CFG 0xb00
66+
67+#define VAL_PKGC_PMON_CFG_RESET 0x38
68+#define VAL_PKGC_PMON_CFG_START 0x7
69+
70+#define IS_PIXEL_BUFFER_PAGES 0x80
71+/*
72+ * when iwake mode is disabled, the critical threshold is statically set
73+ * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4
74+ */
75+#define CRITICAL_THRESHOLD_IWAKE_DISABLE (IS_PIXEL_BUFFER_PAGES * 3 / 4)
76+
77+union fabric_ctrl {
78+ struct {
79+ u16 ltr_val : 10;
80+ u16 ltr_scale : 3;
81+ u16 reserved : 3;
82+ u16 did_val : 10;
83+ u16 did_scale : 3;
84+ u16 reserved2 : 1;
85+ u16 keep_power_in_D0 : 1;
86+ u16 keep_power_override : 1;
87+ } bits;
88+ u32 value;
89+};
90+
91+enum ltr_did_type {
92+ LTR_IWAKE_ON,
93+ LTR_IWAKE_OFF,
94+ LTR_ISYS_ON,
95+ LTR_ISYS_OFF,
96+ LTR_ENHANNCE_IWAKE,
97+ LTR_TYPE_MAX
98+};
99+
100+#define ISYS_PM_QOS_VALUE 300
101+
102+static int isys_isr_one(struct ipu6_bus_device *adev);
103+
104+static int
105+isys_complete_ext_device_registration(struct ipu6_isys *isys,
106+ struct v4l2_subdev *sd,
107+ struct ipu6_isys_csi2_config *csi2)
108+{
109+ struct device *dev = &isys->adev->auxdev.dev;
110+ unsigned int i;
111+ int ret;
112+
113+ for (i = 0; i < sd->entity.num_pads; i++) {
114+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
115+ break;
116+ }
117+
118+ if (i == sd->entity.num_pads) {
119+ dev_warn(dev, "no src pad in external entity\n");
120+ ret = -ENOENT;
121+ goto unregister_subdev;
122+ }
123+
124+ ret = media_create_pad_link(&sd->entity, i,
125+ &isys->csi2[csi2->port].asd.sd.entity,
126+ 0, MEDIA_LNK_FL_ENABLED |
127+ MEDIA_LNK_FL_IMMUTABLE);
128+ if (ret) {
129+ dev_warn(dev, "can't create link\n");
130+ goto unregister_subdev;
131+ }
132+
133+ isys->csi2[csi2->port].nlanes = csi2->nlanes;
134+
135+ return 0;
136+
137+unregister_subdev:
138+ v4l2_device_unregister_subdev(sd);
139+
140+ return ret;
141+}
142+
143+static void isys_stream_init(struct ipu6_isys *isys)
144+{
145+ u32 i;
146+
147+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
148+ mutex_init(&isys->streams[i].mutex);
149+ init_completion(&isys->streams[i].stream_open_completion);
150+ init_completion(&isys->streams[i].stream_close_completion);
151+ init_completion(&isys->streams[i].stream_start_completion);
152+ init_completion(&isys->streams[i].stream_stop_completion);
153+ INIT_LIST_HEAD(&isys->streams[i].queues);
154+ isys->streams[i].isys = isys;
155+ isys->streams[i].stream_handle = i;
156+ isys->streams[i].vc = INVALID_VC_ID;
157+ }
158+}
159+
160+static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys)
161+{
162+ const struct ipu6_isys_internal_csi2_pdata *csi2 =
163+ &isys->pdata->ipdata->csi2;
164+ unsigned int i;
165+
166+ for (i = 0; i < csi2->nports; i++)
167+ ipu6_isys_csi2_cleanup(&isys->csi2[i]);
168+}
169+
170+static int isys_csi2_register_subdevices(struct ipu6_isys *isys)
171+{
172+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
173+ &isys->pdata->ipdata->csi2;
174+ unsigned int i;
175+ int ret;
176+
177+ for (i = 0; i < csi2_pdata->nports; i++) {
178+ ret = ipu6_isys_csi2_init(&isys->csi2[i], isys,
179+ isys->pdata->base +
180+ CSI_REG_PORT_BASE(i), i);
181+ if (ret)
182+ goto fail;
183+
184+ isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
185+ }
186+
187+ return 0;
188+
189+fail:
190+ while (i--)
191+ ipu6_isys_csi2_cleanup(&isys->csi2[i]);
192+
193+ return ret;
194+}
195+
196+static int isys_csi2_create_media_links(struct ipu6_isys *isys)
197+{
198+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
199+ &isys->pdata->ipdata->csi2;
200+ struct device *dev = &isys->adev->auxdev.dev;
201+ unsigned int i, j;
202+ int ret;
203+
204+ for (i = 0; i < csi2_pdata->nports; i++) {
205+ struct media_entity *sd = &isys->csi2[i].asd.sd.entity;
206+
207+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) {
208+ struct ipu6_isys_video *av = &isys->csi2[i].av[j];
209+
210+ ret = media_create_pad_link(sd, CSI2_PAD_SRC + j,
211+ &av->vdev.entity, 0, 0);
212+ if (ret) {
213+ dev_err(dev, "CSI2 can't create link\n");
214+ return ret;
215+ }
216+
217+ av->csi2 = &isys->csi2[i];
218+ }
219+ }
220+
221+ return 0;
222+}
223+
224+static void isys_unregister_video_devices(struct ipu6_isys *isys)
225+{
226+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
227+ &isys->pdata->ipdata->csi2;
228+ unsigned int i, j;
229+
230+ for (i = 0; i < csi2_pdata->nports; i++)
231+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++)
232+ ipu6_isys_video_cleanup(&isys->csi2[i].av[j]);
233+}
234+
235+static int isys_register_video_devices(struct ipu6_isys *isys)
236+{
237+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
238+ &isys->pdata->ipdata->csi2;
239+ unsigned int i, j;
240+ int ret;
241+
242+ for (i = 0; i < csi2_pdata->nports; i++) {
243+ for (j = 0; j < NR_OF_CSI2_SRC_PADS; j++) {
244+ struct ipu6_isys_video *av = &isys->csi2[i].av[j];
245+
246+ snprintf(av->vdev.name, sizeof(av->vdev.name),
247+ IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u",
248+ i * NR_OF_CSI2_SRC_PADS + j);
249+ av->isys = isys;
250+ av->aq.vbq.buf_struct_size =
251+ sizeof(struct ipu6_isys_video_buffer);
252+
253+ ret = ipu6_isys_video_init(av);
254+ if (ret)
255+ goto fail;
256+ }
257+ }
258+
259+ return 0;
260+
261+fail:
262+ while (i--) {
263+ while (j--)
264+ ipu6_isys_video_cleanup(&isys->csi2[i].av[j]);
265+ j = NR_OF_CSI2_SRC_PADS;
266+ }
267+
268+ return ret;
269+}
270+
271+void isys_setup_hw(struct ipu6_isys *isys)
272+{
273+ void __iomem *base = isys->pdata->base;
274+ const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
275+ u32 irqs = 0;
276+ unsigned int i, nports;
277+
278+ nports = isys->pdata->ipdata->csi2.nports;
279+
280+ /* Enable irqs for all MIPI ports */
281+ for (i = 0; i < nports; i++)
282+ irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
283+
284+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge);
285+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp);
286+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask);
287+ writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable);
288+ writel(GENMASK(19, 0),
289+ base + isys->pdata->ipdata->csi2.ctrl0_irq_clear);
290+
291+ irqs = ISYS_UNISPART_IRQS;
292+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE);
293+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE);
294+ writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
295+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
296+ writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE);
297+
298+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
299+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG);
300+
301+ /* Write CDC FIFO threshold values for isys */
302+ for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++)
303+ writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i));
304+}
305+
306+static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
307+{
308+ struct ipu6_isys_stream *stream;
309+ unsigned int i;
310+ u32 status;
311+ int source;
312+
313+ ipu6_isys_register_errors(csi2);
314+
315+ status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
316+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
317+
318+ writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
319+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
320+
321+ source = csi2->asd.source;
322+ for (i = 0; i < NR_OF_CSI2_VC; i++) {
323+ if (status & IPU_CSI_RX_IRQ_FS_VC(i)) {
324+ stream = ipu6_isys_query_stream_by_source(csi2->isys,
325+ source, i);
326+ if (stream) {
327+ ipu6_isys_csi2_sof_event_by_stream(stream);
328+ ipu6_isys_put_stream(stream);
329+ }
330+ }
331+
332+ if (status & IPU_CSI_RX_IRQ_FE_VC(i)) {
333+ stream = ipu6_isys_query_stream_by_source(csi2->isys,
334+ source, i);
335+ if (stream) {
336+ ipu6_isys_csi2_eof_event_by_stream(stream);
337+ ipu6_isys_put_stream(stream);
338+ }
339+ }
340+ }
341+}
342+
343+irqreturn_t isys_isr(struct ipu6_bus_device *adev)
344+{
345+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
346+ void __iomem *base = isys->pdata->base;
347+ u32 status_sw, status_csi;
348+ u32 ctrl0_status, ctrl0_clear;
349+
350+ spin_lock(&isys->power_lock);
351+ if (!isys->power) {
352+ spin_unlock(&isys->power_lock);
353+ return IRQ_NONE;
354+ }
355+
356+ ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status;
357+ ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear;
358+
359+ status_csi = readl(isys->pdata->base + ctrl0_status);
360+ status_sw = readl(isys->pdata->base +
361+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
362+
363+ writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW,
364+ base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
365+
366+ do {
367+ writel(status_csi, isys->pdata->base + ctrl0_clear);
368+
369+ writel(status_sw, isys->pdata->base +
370+ IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
371+
372+ if (isys->isr_csi2_bits & status_csi) {
373+ unsigned int i;
374+
375+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
376+ /* irq from not enabled port */
377+ if (!isys->csi2[i].base)
378+ continue;
379+ if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i))
380+ ipu6_isys_csi2_isr(&isys->csi2[i]);
381+ }
382+ }
383+
384+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
385+
386+ if (!isys_isr_one(adev))
387+ status_sw = IPU6_ISYS_UNISPART_IRQ_SW;
388+ else
389+ status_sw = 0;
390+
391+ status_csi = readl(isys->pdata->base + ctrl0_status);
392+ status_sw |= readl(isys->pdata->base +
393+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
394+ } while ((status_csi & isys->isr_csi2_bits) ||
395+ (status_sw & IPU6_ISYS_UNISPART_IRQ_SW));
396+
397+ writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
398+
399+ spin_unlock(&isys->power_lock);
400+
401+ return IRQ_HANDLED;
402+}
403+
404+static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did)
405+{
406+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
407+ struct ltr_did ltrdid_default;
408+
409+ ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE;
410+ ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE;
411+
412+ if (iwake_watermark->ltrdid.lut_ltr.value)
413+ *pltr_did = iwake_watermark->ltrdid;
414+ else
415+ *pltr_did = ltrdid_default;
416+}
417+
418+static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value)
419+{
420+ struct device *dev = &isys->adev->auxdev.dev;
421+ u32 req_id = index;
422+ u32 offset = 0;
423+ int ret;
424+
425+ ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value);
426+ if (ret)
427+ dev_err(dev, "write %d failed %d", index, ret);
428+
429+ return ret;
430+}
431+
432+/*
433+ * When input system is powered up and before enabling any new sensor capture,
434+ * or after disabling any sensor capture the following values need to be set:
435+ * LTR_value = LTR(usec) from calculation;
436+ * LTR_scale = 2;
437+ * DID_value = DID(usec) from calculation;
438+ * DID_scale = 2;
439+ *
440+ * When input system is powered down, the LTR and DID values
441+ * must be returned to the default values:
442+ * LTR_value = 1023;
443+ * LTR_scale = 5;
444+ * DID_value = 1023;
445+ * DID_scale = 2;
446+ */
447+static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did,
448+ enum ltr_did_type use)
449+{
450+ struct device *dev = &isys->adev->auxdev.dev;
451+ u16 ltr_val, ltr_scale = LTR_SCALE_1024NS;
452+ u16 did_val, did_scale = DID_SCALE_1US;
453+ struct ipu6_device *isp = isys->adev->isp;
454+ union fabric_ctrl fc;
455+
456+ switch (use) {
457+ case LTR_IWAKE_ON:
458+ ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX);
459+ did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX);
460+ ltr_scale = (ltr == LTR_DID_VAL_MAX &&
461+ did == LTR_DID_VAL_MAX) ?
462+ LTR_SCALE_DEFAULT : LTR_SCALE_1024NS;
463+ break;
464+ case LTR_ISYS_ON:
465+ case LTR_IWAKE_OFF:
466+ ltr_val = LTR_DID_PKGC_2R;
467+ did_val = LTR_DID_PKGC_2R;
468+ break;
469+ case LTR_ISYS_OFF:
470+ ltr_val = LTR_DID_VAL_MAX;
471+ did_val = LTR_DID_VAL_MAX;
472+ ltr_scale = LTR_SCALE_DEFAULT;
473+ break;
474+ case LTR_ENHANNCE_IWAKE:
475+ if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) {
476+ ltr_val = LTR_DID_VAL_MAX;
477+ did_val = LTR_DID_VAL_MAX;
478+ ltr_scale = LTR_SCALE_DEFAULT;
479+ } else if (did < ONE_THOUSAND_MICROSECOND) {
480+ ltr_val = ltr;
481+ did_val = did;
482+ } else {
483+ ltr_val = ltr;
484+ /* div 90% value by 32 to account for scale change */
485+ did_val = did / 32;
486+ did_scale = DID_SCALE_32US;
487+ }
488+ break;
489+ default:
490+ ltr_val = LTR_DID_VAL_MAX;
491+ did_val = LTR_DID_VAL_MAX;
492+ ltr_scale = LTR_SCALE_DEFAULT;
493+ break;
494+ }
495+
496+ fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
497+ fc.bits.ltr_val = ltr_val;
498+ fc.bits.ltr_scale = ltr_scale;
499+ fc.bits.did_val = did_val;
500+ fc.bits.did_scale = did_scale;
501+
502+ dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n",
503+ ltr_val, ltr_scale, did_val, did_scale);
504+ writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
505+}
506+
507+/*
508+ * Driver may clear register GDA_ENABLE_IWAKE before FW configures the
509+ * stream for debug purpose. Otherwise driver should not access this register.
510+ */
511+static void enable_iwake(struct ipu6_isys *isys, bool enable)
512+{
513+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
514+ int ret;
515+
516+ mutex_lock(&iwake_watermark->mutex);
517+
518+ if (iwake_watermark->iwake_enabled == enable) {
519+ mutex_unlock(&iwake_watermark->mutex);
520+ return;
521+ }
522+
523+ ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable);
524+ if (!ret)
525+ iwake_watermark->iwake_enabled = enable;
526+
527+ mutex_unlock(&iwake_watermark->mutex);
528+}
529+
530+void update_watermark_setting(struct ipu6_isys *isys)
531+{
532+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
533+ u32 iwake_threshold, iwake_critical_threshold, page_num;
534+ struct device *dev = &isys->adev->auxdev.dev;
535+ u32 calc_fill_time_us = 0, ltr = 0, did = 0;
536+ struct video_stream_watermark *p_watermark;
537+ enum ltr_did_type ltr_did_type;
538+ struct list_head *stream_node;
539+ u64 isys_pb_datarate_mbs = 0;
540+ u32 mem_open_threshold = 0;
541+ struct ltr_did ltrdid;
542+ u64 threshold_bytes;
543+ u32 max_sram_size;
544+ u32 shift;
545+
546+ shift = isys->pdata->ipdata->sram_gran_shift;
547+ max_sram_size = isys->pdata->ipdata->max_sram_size;
548+
549+ mutex_lock(&iwake_watermark->mutex);
550+ if (iwake_watermark->force_iwake_disable) {
551+ set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
552+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
553+ CRITICAL_THRESHOLD_IWAKE_DISABLE);
554+ goto unlock_exit;
555+ }
556+
557+ if (list_empty(&iwake_watermark->video_list)) {
558+ isys_pb_datarate_mbs = 0;
559+ } else {
560+ list_for_each(stream_node, &iwake_watermark->video_list) {
561+ p_watermark = list_entry(stream_node,
562+ struct video_stream_watermark,
563+ stream_node);
564+ isys_pb_datarate_mbs += p_watermark->stream_data_rate;
565+ }
566+ }
567+ mutex_unlock(&iwake_watermark->mutex);
568+
569+ if (!isys_pb_datarate_mbs) {
570+ enable_iwake(isys, false);
571+ set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
572+ mutex_lock(&iwake_watermark->mutex);
573+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
574+ CRITICAL_THRESHOLD_IWAKE_DISABLE);
575+ goto unlock_exit;
576+ }
577+
578+ enable_iwake(isys, true);
579+ calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs;
580+
581+ if (isys->pdata->ipdata->enhanced_iwake) {
582+ ltr = isys->pdata->ipdata->ltr;
583+ did = calc_fill_time_us * DEFAULT_DID_RATIO / 100;
584+ ltr_did_type = LTR_ENHANNCE_IWAKE;
585+ } else {
586+ get_lut_ltrdid(isys, &ltrdid);
587+
588+ if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0)
589+ ltr = 0;
590+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1)
591+ ltr = ltrdid.lut_ltr.bits.val0;
592+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2)
593+ ltr = ltrdid.lut_ltr.bits.val1;
594+ else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3)
595+ ltr = ltrdid.lut_ltr.bits.val2;
596+ else
597+ ltr = ltrdid.lut_ltr.bits.val3;
598+
599+ did = calc_fill_time_us - ltr;
600+ ltr_did_type = LTR_IWAKE_ON;
601+ }
602+
603+ set_iwake_ltrdid(isys, ltr, did, ltr_did_type);
604+
605+ /* calculate iwake threshold with 2KB granularity pages */
606+ threshold_bytes = did * isys_pb_datarate_mbs;
607+ iwake_threshold = max_t(u32, 1, threshold_bytes >> shift);
608+ iwake_threshold = min_t(u32, iwake_threshold, max_sram_size);
609+
610+ mutex_lock(&iwake_watermark->mutex);
611+ if (isys->pdata->ipdata->enhanced_iwake) {
612+ set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
613+ DEFAULT_IWAKE_THRESHOLD);
614+ /* calculate number of pages that will be filled in 10 usec */
615+ page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) /
616+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE;
617+ page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) %
618+ ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0;
619+ mem_open_threshold = isys->pdata->ipdata->memopen_threshold;
620+ mem_open_threshold = max_t(u32, mem_open_threshold, page_num);
621+ dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold);
622+ set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX,
623+ mem_open_threshold);
624+ } else {
625+ set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
626+ iwake_threshold);
627+ }
628+
629+ iwake_critical_threshold = iwake_threshold +
630+ (IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2;
631+
632+ dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold,
633+ iwake_critical_threshold);
634+
635+ set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
636+ iwake_critical_threshold);
637+
638+ writel(VAL_PKGC_PMON_CFG_RESET,
639+ isys->adev->isp->base + REG_PKGC_PMON_CFG);
640+ writel(VAL_PKGC_PMON_CFG_START,
641+ isys->adev->isp->base + REG_PKGC_PMON_CFG);
642+unlock_exit:
643+ mutex_unlock(&iwake_watermark->mutex);
644+}
645+
646+static void isys_iwake_watermark_init(struct ipu6_isys *isys)
647+{
648+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
649+
650+ INIT_LIST_HEAD(&iwake_watermark->video_list);
651+ mutex_init(&iwake_watermark->mutex);
652+
653+ iwake_watermark->ltrdid.lut_ltr.value = 0;
654+ iwake_watermark->isys = isys;
655+ iwake_watermark->iwake_enabled = false;
656+ iwake_watermark->force_iwake_disable = false;
657+}
658+
659+static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys)
660+{
661+ struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
662+
663+ mutex_lock(&iwake_watermark->mutex);
664+ list_del(&iwake_watermark->video_list);
665+ mutex_unlock(&iwake_watermark->mutex);
666+
667+ mutex_destroy(&iwake_watermark->mutex);
668+}
669+
670+/* The .bound() notifier callback when a match is found */
671+static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
672+ struct v4l2_subdev *sd,
673+ struct v4l2_async_connection *asc)
674+{
675+ struct ipu6_isys *isys =
676+ container_of(notifier, struct ipu6_isys, notifier);
677+ struct sensor_async_sd *s_asd =
678+ container_of(asc, struct sensor_async_sd, asc);
679+ int ret;
680+
681+ ret = ipu_bridge_instantiate_vcm(sd->dev);
682+ if (ret) {
683+ dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n");
684+ return ret;
685+ }
686+
687+ dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n",
688+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
689+ ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
690+ if (ret)
691+ return ret;
692+
693+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
694+}
695+
696+static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
697+{
698+ struct ipu6_isys *isys =
699+ container_of(notifier, struct ipu6_isys, notifier);
700+
701+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
702+}
703+
704+static const struct v4l2_async_notifier_operations isys_async_ops = {
705+ .bound = isys_notifier_bound,
706+ .complete = isys_notifier_complete,
707+};
708+
709+#define ISYS_MAX_PORTS 8
710+static int isys_notifier_init(struct ipu6_isys *isys)
711+{
712+ struct ipu6_device *isp = isys->adev->isp;
713+ struct device *dev = &isp->pdev->dev;
714+ unsigned int i;
715+ int ret;
716+
717+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
718+
719+ for (i = 0; i < ISYS_MAX_PORTS; i++) {
720+ struct v4l2_fwnode_endpoint vep = {
721+ .bus_type = V4L2_MBUS_CSI2_DPHY
722+ };
723+ struct sensor_async_sd *s_asd;
724+ struct fwnode_handle *ep;
725+
726+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
727+ FWNODE_GRAPH_ENDPOINT_NEXT);
728+ if (!ep)
729+ continue;
730+
731+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
732+ if (ret) {
733+ dev_err(dev, "fwnode endpoint parse failed: %d\n", ret);
734+ goto err_parse;
735+ }
736+
737+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
738+ struct sensor_async_sd);
739+ if (IS_ERR(s_asd)) {
740+ ret = PTR_ERR(s_asd);
741+ dev_err(dev, "add remove fwnode failed: %d\n", ret);
742+ goto err_parse;
743+ }
744+
745+ s_asd->csi2.port = vep.base.port;
746+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
747+
748+ dev_dbg(dev, "remote endpoint port %d with %d lanes added\n",
749+ s_asd->csi2.port, s_asd->csi2.nlanes);
750+
751+ fwnode_handle_put(ep);
752+
753+ continue;
754+
755+err_parse:
756+ fwnode_handle_put(ep);
757+ return ret;
758+ }
759+
760+ isys->notifier.ops = &isys_async_ops;
761+ ret = v4l2_async_nf_register(&isys->notifier);
762+ if (ret) {
763+ dev_err(dev, "failed to register async notifier : %d\n", ret);
764+ v4l2_async_nf_cleanup(&isys->notifier);
765+ }
766+
767+ return ret;
768+}
769+
770+static void isys_notifier_cleanup(struct ipu6_isys *isys)
771+{
772+ v4l2_async_nf_unregister(&isys->notifier);
773+ v4l2_async_nf_cleanup(&isys->notifier);
774+}
775+
776+static int isys_register_devices(struct ipu6_isys *isys)
777+{
778+ struct device *dev = &isys->adev->auxdev.dev;
779+ struct pci_dev *pdev = isys->adev->isp->pdev;
780+ int ret;
781+
782+ isys->media_dev.dev = dev;
783+ media_device_pci_init(&isys->media_dev,
784+ pdev, IPU6_MEDIA_DEV_MODEL_NAME);
785+
786+ strscpy(isys->v4l2_dev.name, isys->media_dev.model,
787+ sizeof(isys->v4l2_dev.name));
788+
789+ ret = media_device_register(&isys->media_dev);
790+ if (ret < 0)
791+ goto out_media_device_unregister;
792+
793+ isys->v4l2_dev.mdev = &isys->media_dev;
794+ isys->v4l2_dev.ctrl_handler = NULL;
795+
796+ ret = v4l2_device_register(&pdev->dev, &isys->v4l2_dev);
797+ if (ret < 0)
798+ goto out_media_device_unregister;
799+
800+ ret = isys_register_video_devices(isys);
801+ if (ret)
802+ goto out_v4l2_device_unregister;
803+
804+ ret = isys_csi2_register_subdevices(isys);
805+ if (ret)
806+ goto out_isys_unregister_video_device;
807+
808+ ret = isys_csi2_create_media_links(isys);
809+ if (ret)
810+ goto out_isys_unregister_subdevices;
811+
812+ ret = isys_notifier_init(isys);
813+ if (ret)
814+ goto out_isys_unregister_subdevices;
815+
816+ return 0;
817+
818+out_isys_unregister_subdevices:
819+ isys_csi2_unregister_subdevices(isys);
820+
821+out_isys_unregister_video_device:
822+ isys_unregister_video_devices(isys);
823+
824+out_v4l2_device_unregister:
825+ v4l2_device_unregister(&isys->v4l2_dev);
826+
827+out_media_device_unregister:
828+ media_device_unregister(&isys->media_dev);
829+ media_device_cleanup(&isys->media_dev);
830+
831+ dev_err(dev, "failed to register isys devices\n");
832+
833+ return ret;
834+}
835+
836+static void isys_unregister_devices(struct ipu6_isys *isys)
837+{
838+ isys_unregister_video_devices(isys);
839+ isys_csi2_unregister_subdevices(isys);
840+ v4l2_device_unregister(&isys->v4l2_dev);
841+ media_device_unregister(&isys->media_dev);
842+ media_device_cleanup(&isys->media_dev);
843+}
844+
845+static int isys_runtime_pm_resume(struct device *dev)
846+{
847+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
848+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
849+ struct ipu6_device *isp = adev->isp;
850+ unsigned long flags;
851+ int ret;
852+
853+ if (!isys)
854+ return 0;
855+
856+ ret = ipu6_mmu_hw_init(adev->mmu);
857+ if (ret)
858+ return ret;
859+
860+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
861+
862+ ret = ipu6_buttress_start_tsc_sync(isp);
863+ if (ret)
864+ return ret;
865+
866+ spin_lock_irqsave(&isys->power_lock, flags);
867+ isys->power = 1;
868+ spin_unlock_irqrestore(&isys->power_lock, flags);
869+
870+ isys_setup_hw(isys);
871+
872+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
873+
874+ return 0;
875+}
876+
877+static int isys_runtime_pm_suspend(struct device *dev)
878+{
879+ struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
880+ struct ipu6_isys *isys;
881+ unsigned long flags;
882+
883+ isys = dev_get_drvdata(dev);
884+ if (!isys)
885+ return 0;
886+
887+ spin_lock_irqsave(&isys->power_lock, flags);
888+ isys->power = 0;
889+ spin_unlock_irqrestore(&isys->power_lock, flags);
890+
891+ mutex_lock(&isys->mutex);
892+ isys->need_reset = false;
893+ mutex_unlock(&isys->mutex);
894+
895+ isys->phy_termcal_val = 0;
896+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
897+
898+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
899+
900+ ipu6_mmu_hw_cleanup(adev->mmu);
901+
902+ return 0;
903+}
904+
905+static int isys_suspend(struct device *dev)
906+{
907+ struct ipu6_isys *isys = dev_get_drvdata(dev);
908+
909+ /* If stream is open, refuse to suspend */
910+ if (isys->stream_opened)
911+ return -EBUSY;
912+
913+ return 0;
914+}
915+
916+static int isys_resume(struct device *dev)
917+{
918+ return 0;
919+}
920+
921+static const struct dev_pm_ops isys_pm_ops = {
922+ .runtime_suspend = isys_runtime_pm_suspend,
923+ .runtime_resume = isys_runtime_pm_resume,
924+ .suspend = isys_suspend,
925+ .resume = isys_resume,
926+};
927+
928+static void isys_remove(struct auxiliary_device *auxdev)
929+{
930+ struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
931+ struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev);
932+ struct ipu6_device *isp = adev->isp;
933+ struct isys_fw_msgs *fwmsg, *safe;
934+ unsigned int i;
935+
936+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head)
937+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
938+ fwmsg, fwmsg->dma_addr, 0);
939+
940+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head)
941+ dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
942+ fwmsg, fwmsg->dma_addr, 0);
943+
944+ isys_unregister_devices(isys);
945+ isys_notifier_cleanup(isys);
946+
947+ cpu_latency_qos_remove_request(&isys->pm_qos);
948+
949+ if (!isp->secure_mode) {
950+ ipu6_cpd_free_pkg_dir(adev);
951+ ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
952+ release_firmware(adev->fw);
953+ }
954+
955+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
956+ mutex_destroy(&isys->streams[i].mutex);
957+
958+ isys_iwake_watermark_cleanup(isys);
959+ mutex_destroy(&isys->stream_mutex);
960+ mutex_destroy(&isys->mutex);
961+}
962+
963+static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount)
964+{
965+ struct device *dev = &isys->adev->auxdev.dev;
966+ struct isys_fw_msgs *addr;
967+ dma_addr_t dma_addr;
968+ unsigned long flags;
969+ unsigned int i;
970+
971+ for (i = 0; i < amount; i++) {
972+ addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs),
973+ &dma_addr, GFP_KERNEL, 0);
974+ if (!addr)
975+ break;
976+ addr->dma_addr = dma_addr;
977+
978+ spin_lock_irqsave(&isys->listlock, flags);
979+ list_add(&addr->head, &isys->framebuflist);
980+ spin_unlock_irqrestore(&isys->listlock, flags);
981+ }
982+
983+ if (i == amount)
984+ return 0;
985+
986+ spin_lock_irqsave(&isys->listlock, flags);
987+ while (!list_empty(&isys->framebuflist)) {
988+ addr = list_first_entry(&isys->framebuflist,
989+ struct isys_fw_msgs, head);
990+ list_del(&addr->head);
991+ spin_unlock_irqrestore(&isys->listlock, flags);
992+ dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr,
993+ addr->dma_addr, 0);
994+ spin_lock_irqsave(&isys->listlock, flags);
995+ }
996+ spin_unlock_irqrestore(&isys->listlock, flags);
997+
998+ return -ENOMEM;
999+}
1000+
1001+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream)
1002+{
1003+ struct ipu6_isys *isys = stream->isys;
1004+ struct device *dev = &isys->adev->auxdev.dev;
1005+ struct isys_fw_msgs *msg;
1006+ unsigned long flags;
1007+ int ret;
1008+
1009+ spin_lock_irqsave(&isys->listlock, flags);
1010+ if (list_empty(&isys->framebuflist)) {
1011+ spin_unlock_irqrestore(&isys->listlock, flags);
1012+ dev_dbg(dev, "Frame list empty\n");
1013+
1014+ ret = alloc_fw_msg_bufs(isys, 5);
1015+ if (ret < 0)
1016+ return NULL;
1017+
1018+ spin_lock_irqsave(&isys->listlock, flags);
1019+ if (list_empty(&isys->framebuflist)) {
1020+ spin_unlock_irqrestore(&isys->listlock, flags);
1021+ dev_err(dev, "Frame list empty\n");
1022+ return NULL;
1023+ }
1024+ }
1025+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
1026+ list_move(&msg->head, &isys->framebuflist_fw);
1027+ spin_unlock_irqrestore(&isys->listlock, flags);
1028+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
1029+
1030+ return msg;
1031+}
1032+
1033+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys)
1034+{
1035+ struct isys_fw_msgs *fwmsg, *fwmsg0;
1036+ unsigned long flags;
1037+
1038+ spin_lock_irqsave(&isys->listlock, flags);
1039+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head)
1040+ list_move(&fwmsg->head, &isys->framebuflist);
1041+ spin_unlock_irqrestore(&isys->listlock, flags);
1042+}
1043+
1044+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data)
1045+{
1046+ struct isys_fw_msgs *msg;
1047+ unsigned long flags;
1048+ u64 *ptr = (u64 *)data;
1049+
1050+ if (!ptr)
1051+ return;
1052+
1053+ spin_lock_irqsave(&isys->listlock, flags);
1054+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
1055+ list_move(&msg->head, &isys->framebuflist);
1056+ spin_unlock_irqrestore(&isys->listlock, flags);
1057+}
1058+
1059+static int isys_probe(struct auxiliary_device *auxdev,
1060+ const struct auxiliary_device_id *auxdev_id)
1061+{
1062+ const struct ipu6_isys_internal_csi2_pdata *csi2_pdata;
1063+ struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
1064+ struct ipu6_device *isp = adev->isp;
1065+ const struct firmware *fw;
1066+ struct ipu6_isys *isys;
1067+ unsigned int i;
1068+ int ret;
1069+
1070+ if (!isp->bus_ready_to_probe)
1071+ return -EPROBE_DEFER;
1072+
1073+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
1074+ if (!isys)
1075+ return -ENOMEM;
1076+
1077+ adev->auxdrv_data =
1078+ (const struct ipu6_auxdrv_data *)auxdev_id->driver_data;
1079+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
1080+ isys->adev = adev;
1081+ isys->pdata = adev->pdata;
1082+ csi2_pdata = &isys->pdata->ipdata->csi2;
1083+
1084+ isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports,
1085+ sizeof(*isys->csi2), GFP_KERNEL);
1086+ if (!isys->csi2)
1087+ return -ENOMEM;
1088+
1089+ ret = ipu6_mmu_hw_init(adev->mmu);
1090+ if (ret)
1091+ return ret;
1092+
1093+ /* initial sensor type */
1094+ isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
1095+
1096+ spin_lock_init(&isys->streams_lock);
1097+ spin_lock_init(&isys->power_lock);
1098+ isys->power = 0;
1099+ isys->phy_termcal_val = 0;
1100+
1101+ mutex_init(&isys->mutex);
1102+ mutex_init(&isys->stream_mutex);
1103+
1104+ spin_lock_init(&isys->listlock);
1105+ INIT_LIST_HEAD(&isys->framebuflist);
1106+ INIT_LIST_HEAD(&isys->framebuflist_fw);
1107+
1108+ isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN;
1109+ isys->icache_prefetch = 0;
1110+
1111+ dev_set_drvdata(&auxdev->dev, isys);
1112+
1113+ isys_stream_init(isys);
1114+
1115+ if (!isp->secure_mode) {
1116+ fw = isp->cpd_fw;
1117+ ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt);
1118+ if (ret)
1119+ goto release_firmware;
1120+
1121+ ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data);
1122+ if (ret)
1123+ goto remove_shared_buffer;
1124+ }
1125+
1126+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
1127+
1128+ ret = alloc_fw_msg_bufs(isys, 20);
1129+ if (ret < 0)
1130+ goto out_remove_pkg_dir_shared_buffer;
1131+
1132+ isys_iwake_watermark_init(isys);
1133+
1134+ if (is_ipu6se(adev->isp->hw_ver))
1135+ isys->phy_set_power = ipu6_isys_jsl_phy_set_power;
1136+ else if (is_ipu6ep_mtl(adev->isp->hw_ver))
1137+ isys->phy_set_power = ipu6_isys_dwc_phy_set_power;
1138+ else
1139+ isys->phy_set_power = ipu6_isys_mcd_phy_set_power;
1140+
1141+ ret = isys_register_devices(isys);
1142+ if (ret)
1143+ goto out_remove_pkg_dir_shared_buffer;
1144+
1145+ ipu6_mmu_hw_cleanup(adev->mmu);
1146+
1147+ return 0;
1148+
1149+out_remove_pkg_dir_shared_buffer:
1150+ if (!isp->secure_mode)
1151+ ipu6_cpd_free_pkg_dir(adev);
1152+remove_shared_buffer:
1153+ if (!isp->secure_mode)
1154+ ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
1155+release_firmware:
1156+ if (!isp->secure_mode)
1157+ release_firmware(adev->fw);
1158+
1159+ for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
1160+ mutex_destroy(&isys->streams[i].mutex);
1161+
1162+ mutex_destroy(&isys->mutex);
1163+ mutex_destroy(&isys->stream_mutex);
1164+
1165+ ipu6_mmu_hw_cleanup(adev->mmu);
1166+
1167+ return ret;
1168+}
1169+
1170+struct fwmsg {
1171+ int type;
1172+ char *msg;
1173+ bool valid_ts;
1174+};
1175+
1176+static const struct fwmsg fw_msg[] = {
1177+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0},
1178+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0},
1179+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0},
1180+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
1181+ "STREAM_START_AND_CAPTURE_ACK", 0},
1182+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0},
1183+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0},
1184+ {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1},
1185+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0},
1186+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
1187+ "STREAM_START_AND_CAPTURE_DONE", 1},
1188+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1},
1189+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1},
1190+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1},
1191+ {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1},
1192+ {-1, "UNKNOWN MESSAGE", 0}
1193+};
1194+
1195+static u32 resp_type_to_index(int type)
1196+{
1197+ unsigned int i;
1198+
1199+ for (i = 0; i < ARRAY_SIZE(fw_msg); i++)
1200+ if (fw_msg[i].type == type)
1201+ return i;
1202+
1203+ return ARRAY_SIZE(fw_msg) - 1;
1204+}
1205+
1206+static int isys_isr_one(struct ipu6_bus_device *adev)
1207+{
1208+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
1209+ struct ipu6_fw_isys_resp_info_abi *resp;
1210+ struct ipu6_isys_stream *stream;
1211+ struct ipu6_isys_csi2 *csi2 = NULL;
1212+ u32 index;
1213+ u64 ts;
1214+
1215+ if (!isys->fwcom)
1216+ return 1;
1217+
1218+ resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
1219+ if (!resp)
1220+ return 1;
1221+
1222+ ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0];
1223+
1224+ index = resp_type_to_index(resp->type);
1225+ dev_dbg(&adev->auxdev.dev,
1226+ "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n",
1227+ resp->type, fw_msg[index].msg, resp->stream_handle,
1228+ fw_msg[index].valid_ts ? ts : 0, resp->pin_id);
1229+
1230+ if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION)
1231+ /* Suspension is kind of special case: not enough buffers */
1232+ dev_dbg(&adev->auxdev.dev,
1233+ "FW error resp SUSPENSION, details %d\n",
1234+ resp->error_info.error_details);
1235+ else if (resp->error_info.error)
1236+ dev_dbg(&adev->auxdev.dev,
1237+ "FW error resp error %d, details %d\n",
1238+ resp->error_info.error, resp->error_info.error_details);
1239+
1240+ if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) {
1241+ dev_err(&adev->auxdev.dev, "bad stream handle %u\n",
1242+ resp->stream_handle);
1243+ goto leave;
1244+ }
1245+
1246+ stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle);
1247+ if (!stream) {
1248+ dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n",
1249+ resp->stream_handle);
1250+ goto leave;
1251+ }
1252+ stream->error = resp->error_info.error;
1253+
1254+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
1255+
1256+ switch (resp->type) {
1257+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE:
1258+ complete(&stream->stream_open_completion);
1259+ break;
1260+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK:
1261+ complete(&stream->stream_close_completion);
1262+ break;
1263+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK:
1264+ complete(&stream->stream_start_completion);
1265+ break;
1266+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
1267+ complete(&stream->stream_start_completion);
1268+ break;
1269+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK:
1270+ complete(&stream->stream_stop_completion);
1271+ break;
1272+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK:
1273+ complete(&stream->stream_stop_completion);
1274+ break;
1275+ case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY:
1276+ /*
1277+ * firmware only release the capture msg until software
1278+ * get pin_data_ready event
1279+ */
1280+ ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id);
1281+ if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS &&
1282+ stream->output_pins[resp->pin_id].pin_ready)
1283+ stream->output_pins[resp->pin_id].pin_ready(stream,
1284+ resp);
1285+ else
1286+ dev_warn(&adev->auxdev.dev,
1287+ "%d:No data pin ready handler for pin id %d\n",
1288+ resp->stream_handle, resp->pin_id);
1289+ if (csi2)
1290+ ipu6_isys_csi2_error(csi2);
1291+
1292+ break;
1293+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK:
1294+ break;
1295+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
1296+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE:
1297+ break;
1298+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF:
1299+
1300+ ipu6_isys_csi2_sof_event_by_stream(stream);
1301+ stream->seq[stream->seq_index].sequence =
1302+ atomic_read(&stream->sequence) - 1;
1303+ stream->seq[stream->seq_index].timestamp = ts;
1304+ dev_dbg(&adev->auxdev.dev,
1305+ "sof: handle %d: (index %u), timestamp 0x%16.16llx\n",
1306+ resp->stream_handle,
1307+ stream->seq[stream->seq_index].sequence, ts);
1308+ stream->seq_index = (stream->seq_index + 1)
1309+ % IPU6_ISYS_MAX_PARALLEL_SOF;
1310+ break;
1311+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF:
1312+ ipu6_isys_csi2_eof_event_by_stream(stream);
1313+ dev_dbg(&adev->auxdev.dev,
1314+ "eof: handle %d: (index %u), timestamp 0x%16.16llx\n",
1315+ resp->stream_handle,
1316+ stream->seq[stream->seq_index].sequence, ts);
1317+ break;
1318+ case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY:
1319+ break;
1320+ default:
1321+ dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n",
1322+ resp->stream_handle, resp->type);
1323+ break;
1324+ }
1325+
1326+ ipu6_isys_put_stream(stream);
1327+leave:
1328+ ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
1329+ return 0;
1330+}
1331+
1332+static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = {
1333+ .isr = isys_isr,
1334+ .isr_threaded = NULL,
1335+ .wake_isr_thread = false,
1336+};
1337+
1338+static const struct auxiliary_device_id ipu6_isys_id_table[] = {
1339+ {
1340+ .name = "intel_ipu6.isys",
1341+ .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data,
1342+ },
1343+ { }
1344+};
1345+MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table);
1346+
1347+static struct auxiliary_driver isys_driver = {
1348+ .name = IPU6_ISYS_NAME,
1349+ .probe = isys_probe,
1350+ .remove = isys_remove,
1351+ .id_table = ipu6_isys_id_table,
1352+ .driver = {
1353+ .pm = &isys_pm_ops,
1354+ },
1355+};
1356+
1357+module_auxiliary_driver(isys_driver);
1358+
1359+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
1360+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
1361+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
1362+MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
1363+MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
1364+MODULE_LICENSE("GPL");
1365+MODULE_DESCRIPTION("Intel IPU6 input system driver");
1366+MODULE_IMPORT_NS(INTEL_IPU6);
1367+MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
drivers/media/pci/intel/ipu6/ipu6-isys.h+206−0
@@ -0,0 +1,206 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_ISYS_H
5+#define IPU6_ISYS_H
6+
7+#include <linux/irqreturn.h>
8+#include <linux/list.h>
9+#include <linux/mutex.h>
10+#include <linux/pm_qos.h>
11+#include <linux/spinlock_types.h>
12+#include <linux/types.h>
13+
14+#include <media/media-device.h>
15+#include <media/v4l2-async.h>
16+#include <media/v4l2-device.h>
17+
18+#include "ipu6.h"
19+#include "ipu6-fw-isys.h"
20+#include "ipu6-isys-csi2.h"
21+#include "ipu6-isys-video.h"
22+
23+struct ipu6_bus_device;
24+
25+#define IPU6_ISYS_ENTITY_PREFIX "Intel IPU6"
26+/* FW support max 16 streams */
27+#define IPU6_ISYS_MAX_STREAMS 16
28+#define ISYS_UNISPART_IRQS (IPU6_ISYS_UNISPART_IRQ_SW | \
29+ IPU6_ISYS_UNISPART_IRQ_CSI0 | \
30+ IPU6_ISYS_UNISPART_IRQ_CSI1)
31+
32+#define IPU6_ISYS_2600_MEM_LINE_ALIGN 64
33+
34+/*
35+ * Current message queue configuration. These must be big enough
36+ * so that they never gets full. Queues are located in system memory
37+ */
38+#define IPU6_ISYS_SIZE_RECV_QUEUE 40
39+#define IPU6_ISYS_SIZE_SEND_QUEUE 40
40+#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5
41+#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5
42+#define IPU6_ISYS_NUM_RECV_QUEUE 1
43+
44+#define IPU6_ISYS_MIN_WIDTH 2U
45+#define IPU6_ISYS_MIN_HEIGHT 2U
46+#define IPU6_ISYS_MAX_WIDTH 4672U
47+#define IPU6_ISYS_MAX_HEIGHT 3416U
48+
49+/* the threshold granularity is 2KB on IPU6 */
50+#define IPU6_SRAM_GRANULARITY_SHIFT 11
51+#define IPU6_SRAM_GRANULARITY_SIZE 2048
52+/* the threshold granularity is 1KB on IPU6SE */
53+#define IPU6SE_SRAM_GRANULARITY_SHIFT 10
54+#define IPU6SE_SRAM_GRANULARITY_SIZE 1024
55+/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */
56+#define IPU6_MAX_SRAM_SIZE (200 << 10)
57+/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */
58+#define IPU6SE_MAX_SRAM_SIZE (96 << 10)
59+
60+#define IPU6EP_LTR_VALUE 200
61+#define IPU6EP_MIN_MEMOPEN_TH 0x4
62+#define IPU6EP_MTL_LTR_VALUE 1023
63+#define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc
64+
65+struct ltr_did {
66+ union {
67+ u32 value;
68+ struct {
69+ u8 val0;
70+ u8 val1;
71+ u8 val2;
72+ u8 val3;
73+ } bits;
74+ } lut_ltr;
75+ union {
76+ u32 value;
77+ struct {
78+ u8 th0;
79+ u8 th1;
80+ u8 th2;
81+ u8 th3;
82+ } bits;
83+ } lut_fill_time;
84+};
85+
86+struct isys_iwake_watermark {
87+ bool iwake_enabled;
88+ bool force_iwake_disable;
89+ u32 iwake_threshold;
90+ u64 isys_pixelbuffer_datarate;
91+ struct ltr_did ltrdid;
92+ struct mutex mutex; /* protect whole struct */
93+ struct ipu6_isys *isys;
94+ struct list_head video_list;
95+};
96+
97+struct ipu6_isys_csi2_config {
98+ u32 nlanes;
99+ u32 port;
100+};
101+
102+struct sensor_async_sd {
103+ struct v4l2_async_connection asc;
104+ struct ipu6_isys_csi2_config csi2;
105+};
106+
107+/*
108+ * struct ipu6_isys
109+ *
110+ * @media_dev: Media device
111+ * @v4l2_dev: V4L2 device
112+ * @adev: ISYS bus device
113+ * @power: Is ISYS powered on or not?
114+ * @isr_bits: Which bits does the ISR handle?
115+ * @power_lock: Serialise access to power (power state in general)
116+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
117+ * @streams_lock: serialise access to streams
118+ * @streams: streams per firmware stream ID
119+ * @fwcom: fw communication layer private pointer
120+ * or optional external library private pointer
121+ * @line_align: line alignment in memory
122+ * @phy_termcal_val: the termination calibration value, only used for DWC PHY
123+ * @need_reset: Isys requires d0i0->i3 transition
124+ * @ref_count: total number of callers fw open
125+ * @mutex: serialise access isys video open/release related operations
126+ * @stream_mutex: serialise stream start and stop, queueing requests
127+ * @pdata: platform data pointer
128+ * @csi2: CSI-2 receivers
129+ */
130+struct ipu6_isys {
131+ struct media_device media_dev;
132+ struct v4l2_device v4l2_dev;
133+ struct ipu6_bus_device *adev;
134+
135+ int power;
136+ spinlock_t power_lock;
137+ u32 isr_csi2_bits;
138+ u32 csi2_rx_ctrl_cached;
139+ spinlock_t streams_lock;
140+ struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS];
141+ int streams_ref_count[IPU6_ISYS_MAX_STREAMS];
142+ void *fwcom;
143+ unsigned int line_align;
144+ u32 phy_termcal_val;
145+ bool need_reset;
146+ bool icache_prefetch;
147+ bool csi2_cse_ipc_not_supported;
148+ unsigned int ref_count;
149+ unsigned int stream_opened;
150+ unsigned int sensor_type;
151+
152+ struct mutex mutex;
153+ struct mutex stream_mutex;
154+
155+ struct ipu6_isys_pdata *pdata;
156+
157+ int (*phy_set_power)(struct ipu6_isys *isys,
158+ struct ipu6_isys_csi2_config *cfg,
159+ const struct ipu6_isys_csi2_timing *timing,
160+ bool on);
161+
162+ struct ipu6_isys_csi2 *csi2;
163+
164+ struct pm_qos_request pm_qos;
165+ spinlock_t listlock; /* Protect framebuflist */
166+ struct list_head framebuflist;
167+ struct list_head framebuflist_fw;
168+ struct v4l2_async_notifier notifier;
169+ struct isys_iwake_watermark iwake_watermark;
170+};
171+
172+struct isys_fw_msgs {
173+ union {
174+ u64 dummy;
175+ struct ipu6_fw_isys_frame_buff_set_abi frame;
176+ struct ipu6_fw_isys_stream_cfg_data_abi stream;
177+ } fw_msg;
178+ struct list_head head;
179+ dma_addr_t dma_addr;
180+};
181+
182+struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream);
183+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data);
184+void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
185+
186+extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
187+
188+void isys_setup_hw(struct ipu6_isys *isys);
189+irqreturn_t isys_isr(struct ipu6_bus_device *adev);
190+void update_watermark_setting(struct ipu6_isys *isys);
191+
192+int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
193+ struct ipu6_isys_csi2_config *cfg,
194+ const struct ipu6_isys_csi2_timing *timing,
195+ bool on);
196+
197+int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
198+ struct ipu6_isys_csi2_config *cfg,
199+ const struct ipu6_isys_csi2_timing *timing,
200+ bool on);
201+
202+int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
203+ struct ipu6_isys_csi2_config *cfg,
204+ const struct ipu6_isys_csi2_timing *timing,
205+ bool on);
206+#endif /* IPU6_ISYS_H */
drivers/media/pci/intel/ipu6/ipu6-mmu.c+846−0
@@ -0,0 +1,846 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+#include <asm/barrier.h>
6+
7+#include <linux/align.h>
8+#include <linux/atomic.h>
9+#include <linux/bitops.h>
10+#include <linux/bits.h>
11+#include <linux/bug.h>
12+#include <linux/cacheflush.h>
13+#include <linux/dma-mapping.h>
14+#include <linux/err.h>
15+#include <linux/gfp.h>
16+#include <linux/io.h>
17+#include <linux/iova.h>
18+#include <linux/math.h>
19+#include <linux/minmax.h>
20+#include <linux/mm.h>
21+#include <linux/pfn.h>
22+#include <linux/slab.h>
23+#include <linux/spinlock.h>
24+#include <linux/types.h>
25+#include <linux/vmalloc.h>
26+
27+#include "ipu6.h"
28+#include "ipu6-dma.h"
29+#include "ipu6-mmu.h"
30+#include "ipu6-platform-regs.h"
31+
32+#define ISP_PAGE_SHIFT 12
33+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT)
34+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1))
35+
36+#define ISP_L1PT_SHIFT 22
37+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1))
38+
39+#define ISP_L2PT_SHIFT 12
40+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK))))
41+
42+#define ISP_L1PT_PTES 1024
43+#define ISP_L2PT_PTES 1024
44+
45+#define ISP_PADDR_SHIFT 12
46+
47+#define REG_TLB_INVALIDATE 0x0000
48+
49+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */
50+#define REG_INFO 0x0008
51+
52+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT)
53+
54+static void tlb_invalidate(struct ipu6_mmu *mmu)
55+{
56+ unsigned long flags;
57+ unsigned int i;
58+
59+ spin_lock_irqsave(&mmu->ready_lock, flags);
60+ if (!mmu->ready) {
61+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
62+ return;
63+ }
64+
65+ for (i = 0; i < mmu->nr_mmus; i++) {
66+ /*
67+ * To avoid the HW bug induced dead lock in some of the IPU6
68+ * MMUs on successive invalidate calls, we need to first do a
69+ * read to the page table base before writing the invalidate
70+ * register. MMUs which need to implement this WA, will have
71+ * the insert_read_before_invalidate flags set as true.
72+ * Disregard the return value of the read.
73+ */
74+ if (mmu->mmu_hw[i].insert_read_before_invalidate)
75+ readl(mmu->mmu_hw[i].base + REG_L1_PHYS);
76+
77+ writel(0xffffffff, mmu->mmu_hw[i].base +
78+ REG_TLB_INVALIDATE);
79+ /*
80+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
81+ * When the actual MMIO write reaches the IPU6 TLB Invalidate
82+ * register, wmb() will force the TLB invalidate out if the CPU
83+ * attempts to update the IOMMU page table (or sooner).
84+ */
85+ wmb();
86+ }
87+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
88+}
89+
90+#ifdef DEBUG
91+static void page_table_dump(struct ipu6_mmu_info *mmu_info)
92+{
93+ u32 l1_idx;
94+
95+ dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n");
96+
97+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
98+ u32 l2_idx;
99+ u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT;
100+
101+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval)
102+ continue;
103+ dev_dbg(mmu_info->dev,
104+ "l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n",
105+ l1_idx, iova, iova + ISP_PAGE_SIZE,
106+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]));
107+
108+ for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) {
109+ u32 *l2_pt = mmu_info->l2_pts[l1_idx];
110+ u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT);
111+
112+ if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval)
113+ continue;
114+
115+ dev_dbg(mmu_info->dev,
116+ "\tl2 entry %u; iova 0x%8.8x, phys %pa\n",
117+ l2_idx, iova2,
118+ TBL_PHYS_ADDR(l2_pt[l2_idx]));
119+ }
120+ }
121+
122+ dev_dbg(mmu_info->dev, "end IOMMU page table dump\n");
123+}
124+#endif /* DEBUG */
125+
126+static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr)
127+{
128+ dma_addr_t dma;
129+
130+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL);
131+ if (dma_mapping_error(mmu_info->dev, dma))
132+ return 0;
133+
134+ return dma;
135+}
136+
137+static int get_dummy_page(struct ipu6_mmu_info *mmu_info)
138+{
139+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
140+ dma_addr_t dma;
141+
142+ if (!pt)
143+ return -ENOMEM;
144+
145+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt);
146+
147+ dma = map_single(mmu_info, pt);
148+ if (!dma) {
149+ dev_err(mmu_info->dev, "Failed to map dummy page\n");
150+ goto err_free_page;
151+ }
152+
153+ mmu_info->dummy_page = pt;
154+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT;
155+
156+ return 0;
157+
158+err_free_page:
159+ free_page((unsigned long)pt);
160+ return -ENOMEM;
161+}
162+
163+static void free_dummy_page(struct ipu6_mmu_info *mmu_info)
164+{
165+ dma_unmap_single(mmu_info->dev,
166+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval),
167+ PAGE_SIZE, DMA_BIDIRECTIONAL);
168+ free_page((unsigned long)mmu_info->dummy_page);
169+}
170+
171+static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
172+{
173+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
174+ dma_addr_t dma;
175+ unsigned int i;
176+
177+ if (!pt)
178+ return -ENOMEM;
179+
180+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt);
181+
182+ dma = map_single(mmu_info, pt);
183+ if (!dma) {
184+ dev_err(mmu_info->dev, "Failed to map l2pt page\n");
185+ goto err_free_page;
186+ }
187+
188+ for (i = 0; i < ISP_L2PT_PTES; i++)
189+ pt[i] = mmu_info->dummy_page_pteval;
190+
191+ mmu_info->dummy_l2_pt = pt;
192+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT;
193+
194+ return 0;
195+
196+err_free_page:
197+ free_page((unsigned long)pt);
198+ return -ENOMEM;
199+}
200+
201+static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
202+{
203+ dma_unmap_single(mmu_info->dev,
204+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval),
205+ PAGE_SIZE, DMA_BIDIRECTIONAL);
206+ free_page((unsigned long)mmu_info->dummy_l2_pt);
207+}
208+
209+static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info)
210+{
211+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
212+ dma_addr_t dma;
213+ unsigned int i;
214+
215+ if (!pt)
216+ return NULL;
217+
218+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt);
219+
220+ for (i = 0; i < ISP_L1PT_PTES; i++)
221+ pt[i] = mmu_info->dummy_l2_pteval;
222+
223+ dma = map_single(mmu_info, pt);
224+ if (!dma) {
225+ dev_err(mmu_info->dev, "Failed to map l1pt page\n");
226+ goto err_free_page;
227+ }
228+
229+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT;
230+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma);
231+
232+ return pt;
233+
234+err_free_page:
235+ free_page((unsigned long)pt);
236+ return NULL;
237+}
238+
239+static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info)
240+{
241+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
242+ unsigned int i;
243+
244+ if (!pt)
245+ return NULL;
246+
247+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
248+
249+ for (i = 0; i < ISP_L1PT_PTES; i++)
250+ pt[i] = mmu_info->dummy_page_pteval;
251+
252+ return pt;
253+}
254+
255+static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
256+ phys_addr_t paddr, size_t size)
257+{
258+ u32 l1_idx = iova >> ISP_L1PT_SHIFT;
259+ u32 iova_start = iova;
260+ u32 *l2_pt, *l2_virt;
261+ unsigned int l2_idx;
262+ unsigned long flags;
263+ dma_addr_t dma;
264+ u32 l1_entry;
265+
266+ dev_dbg(mmu_info->dev,
267+ "mapping l2 page table for l1 index %u (iova %8.8x)\n",
268+ l1_idx, (u32)iova);
269+
270+ spin_lock_irqsave(&mmu_info->lock, flags);
271+ l1_entry = mmu_info->l1_pt[l1_idx];
272+ if (l1_entry == mmu_info->dummy_l2_pteval) {
273+ l2_virt = mmu_info->l2_pts[l1_idx];
274+ if (likely(!l2_virt)) {
275+ l2_virt = alloc_l2_pt(mmu_info);
276+ if (!l2_virt) {
277+ spin_unlock_irqrestore(&mmu_info->lock, flags);
278+ return -ENOMEM;
279+ }
280+ }
281+
282+ dma = map_single(mmu_info, l2_virt);
283+ if (!dma) {
284+ dev_err(mmu_info->dev, "Failed to map l2pt page\n");
285+ free_page((unsigned long)l2_virt);
286+ spin_unlock_irqrestore(&mmu_info->lock, flags);
287+ return -EINVAL;
288+ }
289+
290+ l1_entry = dma >> ISP_PADDR_SHIFT;
291+
292+ dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n",
293+ l1_idx, l2_virt);
294+ mmu_info->l1_pt[l1_idx] = l1_entry;
295+ mmu_info->l2_pts[l1_idx] = l2_virt;
296+ clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx],
297+ sizeof(mmu_info->l1_pt[l1_idx]));
298+ }
299+
300+ l2_pt = mmu_info->l2_pts[l1_idx];
301+
302+ dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry);
303+
304+ paddr = ALIGN(paddr, ISP_PAGE_SIZE);
305+
306+ l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
307+
308+ dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx,
309+ l2_pt[l2_idx]);
310+ if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) {
311+ spin_unlock_irqrestore(&mmu_info->lock, flags);
312+ return -EINVAL;
313+ }
314+
315+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT;
316+
317+ clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx]));
318+ spin_unlock_irqrestore(&mmu_info->lock, flags);
319+
320+ dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx,
321+ l2_pt[l2_idx]);
322+
323+ return 0;
324+}
325+
326+static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
327+ phys_addr_t paddr, size_t size)
328+{
329+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE);
330+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE);
331+
332+ dev_dbg(mmu_info->dev,
333+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n",
334+ iova_start, iova_end, size, paddr);
335+
336+ return l2_map(mmu_info, iova_start, paddr, size);
337+}
338+
339+static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
340+ phys_addr_t dummy, size_t size)
341+{
342+ u32 l1_idx = iova >> ISP_L1PT_SHIFT;
343+ u32 iova_start = iova;
344+ unsigned int l2_idx;
345+ size_t unmapped = 0;
346+ unsigned long flags;
347+ u32 *l2_pt;
348+
349+ dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n",
350+ l1_idx, iova);
351+
352+ spin_lock_irqsave(&mmu_info->lock, flags);
353+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) {
354+ spin_unlock_irqrestore(&mmu_info->lock, flags);
355+ dev_err(mmu_info->dev,
356+ "unmap iova 0x%8.8lx l1 idx %u which was not mapped\n",
357+ iova, l1_idx);
358+ return 0;
359+ }
360+
361+ for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
362+ (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT)
363+ < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) {
364+ l2_pt = mmu_info->l2_pts[l1_idx];
365+ dev_dbg(mmu_info->dev,
366+ "unmap l2 index %u with pteval 0x%10.10llx\n",
367+ l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx]));
368+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval;
369+
370+ clflush_cache_range((void *)&l2_pt[l2_idx],
371+ sizeof(l2_pt[l2_idx]));
372+ unmapped++;
373+ }
374+ spin_unlock_irqrestore(&mmu_info->lock, flags);
375+
376+ return unmapped << ISP_PAGE_SHIFT;
377+}
378+
379+static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info,
380+ unsigned long iova, size_t size)
381+{
382+ return l2_unmap(mmu_info, iova, 0, size);
383+}
384+
385+static int allocate_trash_buffer(struct ipu6_mmu *mmu)
386+{
387+ unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE));
388+ struct iova *iova;
389+ unsigned int i;
390+ dma_addr_t dma;
391+ unsigned long iova_addr;
392+ int ret;
393+
394+ /* Allocate 8MB in iova range */
395+ iova = alloc_iova(&mmu->dmap->iovad, n_pages,
396+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
397+ if (!iova) {
398+ dev_err(mmu->dev, "cannot allocate iova range for trash\n");
399+ return -ENOMEM;
400+ }
401+
402+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0,
403+ PAGE_SIZE, DMA_BIDIRECTIONAL);
404+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) {
405+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n");
406+ ret = -ENOMEM;
407+ goto out_free_iova;
408+ }
409+
410+ mmu->pci_trash_page = dma;
411+
412+ /*
413+ * Map the 8MB iova address range to the same physical trash page
414+ * mmu->trash_page which is already reserved at the probe
415+ */
416+ iova_addr = iova->pfn_lo;
417+ for (i = 0; i < n_pages; i++) {
418+ ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
419+ mmu->pci_trash_page, PAGE_SIZE);
420+ if (ret) {
421+ dev_err(mmu->dev,
422+ "mapping trash buffer range failed\n");
423+ goto out_unmap;
424+ }
425+
426+ iova_addr++;
427+ }
428+
429+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo);
430+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n",
431+ mmu->mmid, (unsigned int)mmu->iova_trash_page);
432+ return 0;
433+
434+out_unmap:
435+ ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
436+ PFN_PHYS(iova_size(iova)));
437+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page,
438+ PAGE_SIZE, DMA_BIDIRECTIONAL);
439+out_free_iova:
440+ __free_iova(&mmu->dmap->iovad, iova);
441+ return ret;
442+}
443+
444+int ipu6_mmu_hw_init(struct ipu6_mmu *mmu)
445+{
446+ struct ipu6_mmu_info *mmu_info;
447+ unsigned long flags;
448+ unsigned int i;
449+
450+ mmu_info = mmu->dmap->mmu_info;
451+
452+ /* Initialise the each MMU HW block */
453+ for (i = 0; i < mmu->nr_mmus; i++) {
454+ struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
455+ unsigned int j;
456+ u16 block_addr;
457+
458+ /* Write page table address per MMU */
459+ writel((phys_addr_t)mmu_info->l1_pt_dma,
460+ mmu->mmu_hw[i].base + REG_L1_PHYS);
461+
462+ /* Set info bits per MMU */
463+ writel(mmu->mmu_hw[i].info_bits,
464+ mmu->mmu_hw[i].base + REG_INFO);
465+
466+ /* Configure MMU TLB stream configuration for L1 */
467+ for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams;
468+ block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) {
469+ if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) {
470+ dev_err(mmu->dev, "invalid L1 configuration\n");
471+ return -EINVAL;
472+ }
473+
474+ /* Write block start address for each streams */
475+ writel(block_addr, mmu_hw->base +
476+ mmu_hw->l1_stream_id_reg_offset + 4 * j);
477+ }
478+
479+ /* Configure MMU TLB stream configuration for L2 */
480+ for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams;
481+ block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) {
482+ if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) {
483+ dev_err(mmu->dev, "invalid L2 configuration\n");
484+ return -EINVAL;
485+ }
486+
487+ writel(block_addr, mmu_hw->base +
488+ mmu_hw->l2_stream_id_reg_offset + 4 * j);
489+ }
490+ }
491+
492+ if (!mmu->trash_page) {
493+ int ret;
494+
495+ mmu->trash_page = alloc_page(GFP_KERNEL);
496+ if (!mmu->trash_page) {
497+ dev_err(mmu->dev, "insufficient memory for trash buffer\n");
498+ return -ENOMEM;
499+ }
500+
501+ ret = allocate_trash_buffer(mmu);
502+ if (ret) {
503+ __free_page(mmu->trash_page);
504+ mmu->trash_page = NULL;
505+ dev_err(mmu->dev, "trash buffer allocation failed\n");
506+ return ret;
507+ }
508+ }
509+
510+ spin_lock_irqsave(&mmu->ready_lock, flags);
511+ mmu->ready = true;
512+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
513+
514+ return 0;
515+}
516+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6);
517+
518+static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
519+{
520+ struct ipu6_mmu_info *mmu_info;
521+ int ret;
522+
523+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL);
524+ if (!mmu_info)
525+ return NULL;
526+
527+ mmu_info->aperture_start = 0;
528+ mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ?
529+ IPU6_MMU_ADDR_BITS :
530+ IPU6_MMU_ADDR_BITS_NON_SECURE);
531+ mmu_info->pgsize_bitmap = SZ_4K;
532+ mmu_info->dev = &isp->pdev->dev;
533+
534+ ret = get_dummy_page(mmu_info);
535+ if (ret)
536+ goto err_free_info;
537+
538+ ret = alloc_dummy_l2_pt(mmu_info);
539+ if (ret)
540+ goto err_free_dummy_page;
541+
542+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts));
543+ if (!mmu_info->l2_pts)
544+ goto err_free_dummy_l2_pt;
545+
546+ /*
547+ * We always map the L1 page table (a single page as well as
548+ * the L2 page tables).
549+ */
550+ mmu_info->l1_pt = alloc_l1_pt(mmu_info);
551+ if (!mmu_info->l1_pt)
552+ goto err_free_l2_pts;
553+
554+ spin_lock_init(&mmu_info->lock);
555+
556+ dev_dbg(mmu_info->dev, "domain initialised\n");
557+
558+ return mmu_info;
559+
560+err_free_l2_pts:
561+ vfree(mmu_info->l2_pts);
562+err_free_dummy_l2_pt:
563+ free_dummy_l2_pt(mmu_info);
564+err_free_dummy_page:
565+ free_dummy_page(mmu_info);
566+err_free_info:
567+ kfree(mmu_info);
568+
569+ return NULL;
570+}
571+
572+void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu)
573+{
574+ unsigned long flags;
575+
576+ spin_lock_irqsave(&mmu->ready_lock, flags);
577+ mmu->ready = false;
578+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
579+}
580+EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6);
581+
582+static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
583+{
584+ struct ipu6_dma_mapping *dmap;
585+
586+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL);
587+ if (!dmap)
588+ return NULL;
589+
590+ dmap->mmu_info = ipu6_mmu_alloc(isp);
591+ if (!dmap->mmu_info) {
592+ kfree(dmap);
593+ return NULL;
594+ }
595+
596+ init_iova_domain(&dmap->iovad, SZ_4K, 1);
597+ dmap->mmu_info->dmap = dmap;
598+
599+ dev_dbg(&isp->pdev->dev, "alloc mapping\n");
600+
601+ iova_cache_get();
602+
603+ return dmap;
604+}
605+
606+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
607+ dma_addr_t iova)
608+{
609+ phys_addr_t phy_addr;
610+ unsigned long flags;
611+ u32 *l2_pt;
612+
613+ spin_lock_irqsave(&mmu_info->lock, flags);
614+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT];
615+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT];
616+ phy_addr <<= ISP_PAGE_SHIFT;
617+ spin_unlock_irqrestore(&mmu_info->lock, flags);
618+
619+ return phy_addr;
620+}
621+
622+static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap,
623+ unsigned long addr_merge, size_t size)
624+{
625+ unsigned int pgsize_idx;
626+ size_t pgsize;
627+
628+ /* Max page size that still fits into 'size' */
629+ pgsize_idx = __fls(size);
630+
631+ if (likely(addr_merge)) {
632+ /* Max page size allowed by address */
633+ unsigned int align_pgsize_idx = __ffs(addr_merge);
634+
635+ pgsize_idx = min(pgsize_idx, align_pgsize_idx);
636+ }
637+
638+ pgsize = (1UL << (pgsize_idx + 1)) - 1;
639+ pgsize &= pgsize_bitmap;
640+
641+ WARN_ON(!pgsize);
642+
643+ /* pick the biggest page */
644+ pgsize_idx = __fls(pgsize);
645+ pgsize = 1UL << pgsize_idx;
646+
647+ return pgsize;
648+}
649+
650+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
651+ size_t size)
652+{
653+ size_t unmapped_page, unmapped = 0;
654+ unsigned int min_pagesz;
655+
656+ /* find out the minimum page size supported */
657+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
658+
659+ /*
660+ * The virtual address and the size of the mapping must be
661+ * aligned (at least) to the size of the smallest page supported
662+ * by the hardware
663+ */
664+ if (!IS_ALIGNED(iova | size, min_pagesz)) {
665+ dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
666+ iova, size, min_pagesz);
667+ return -EINVAL;
668+ }
669+
670+ /*
671+ * Keep iterating until we either unmap 'size' bytes (or more)
672+ * or we hit an area that isn't mapped.
673+ */
674+ while (unmapped < size) {
675+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
676+ iova, size - unmapped);
677+
678+ unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize);
679+ if (!unmapped_page)
680+ break;
681+
682+ dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n",
683+ iova, unmapped_page);
684+
685+ iova += unmapped_page;
686+ unmapped += unmapped_page;
687+ }
688+
689+ return unmapped;
690+}
691+
692+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
693+ phys_addr_t paddr, size_t size)
694+{
695+ unsigned long orig_iova = iova;
696+ unsigned int min_pagesz;
697+ size_t orig_size = size;
698+ int ret = 0;
699+
700+ if (mmu_info->pgsize_bitmap == 0UL)
701+ return -ENODEV;
702+
703+ /* find out the minimum page size supported */
704+ min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
705+
706+ /*
707+ * both the virtual address and the physical one, as well as
708+ * the size of the mapping, must be aligned (at least) to the
709+ * size of the smallest page supported by the hardware
710+ */
711+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
712+ dev_err(mmu_info->dev,
713+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n",
714+ iova, &paddr, size, min_pagesz);
715+ return -EINVAL;
716+ }
717+
718+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
719+ iova, &paddr, size);
720+
721+ while (size) {
722+ size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
723+ iova | paddr, size);
724+
725+ dev_dbg(mmu_info->dev,
726+ "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
727+ iova, &paddr, pgsize);
728+
729+ ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize);
730+ if (ret)
731+ break;
732+
733+ iova += pgsize;
734+ paddr += pgsize;
735+ size -= pgsize;
736+ }
737+
738+ /* unroll mapping in case something went wrong */
739+ if (ret)
740+ ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size);
741+
742+ return ret;
743+}
744+
745+static void ipu6_mmu_destroy(struct ipu6_mmu *mmu)
746+{
747+ struct ipu6_dma_mapping *dmap = mmu->dmap;
748+ struct ipu6_mmu_info *mmu_info = dmap->mmu_info;
749+ struct iova *iova;
750+ u32 l1_idx;
751+
752+ if (mmu->iova_trash_page) {
753+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page));
754+ if (iova) {
755+ /* unmap and free the trash buffer iova */
756+ ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo),
757+ PFN_PHYS(iova_size(iova)));
758+ __free_iova(&dmap->iovad, iova);
759+ } else {
760+ dev_err(mmu->dev, "trash buffer iova not found.\n");
761+ }
762+
763+ mmu->iova_trash_page = 0;
764+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page,
765+ PAGE_SIZE, DMA_BIDIRECTIONAL);
766+ mmu->pci_trash_page = 0;
767+ __free_page(mmu->trash_page);
768+ }
769+
770+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
771+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) {
772+ dma_unmap_single(mmu_info->dev,
773+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]),
774+ PAGE_SIZE, DMA_BIDIRECTIONAL);
775+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]);
776+ }
777+ }
778+
779+ vfree(mmu_info->l2_pts);
780+ free_dummy_page(mmu_info);
781+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma),
782+ PAGE_SIZE, DMA_BIDIRECTIONAL);
783+ free_page((unsigned long)mmu_info->dummy_l2_pt);
784+ free_page((unsigned long)mmu_info->l1_pt);
785+ kfree(mmu_info);
786+}
787+
788+struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
789+ void __iomem *base, int mmid,
790+ const struct ipu6_hw_variants *hw)
791+{
792+ struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev));
793+ struct ipu6_mmu_pdata *pdata;
794+ struct ipu6_mmu *mmu;
795+ unsigned int i;
796+
797+ if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES)
798+ return ERR_PTR(-EINVAL);
799+
800+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
801+ if (!pdata)
802+ return ERR_PTR(-ENOMEM);
803+
804+ for (i = 0; i < hw->nr_mmus; i++) {
805+ struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i];
806+ const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i];
807+
808+ if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS ||
809+ src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS)
810+ return ERR_PTR(-EINVAL);
811+
812+ *pdata_mmu = *src_mmu;
813+ pdata_mmu->base = base + src_mmu->offset;
814+ }
815+
816+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
817+ if (!mmu)
818+ return ERR_PTR(-ENOMEM);
819+
820+ mmu->mmid = mmid;
821+ mmu->mmu_hw = pdata->mmu_hw;
822+ mmu->nr_mmus = hw->nr_mmus;
823+ mmu->tlb_invalidate = tlb_invalidate;
824+ mmu->ready = false;
825+ INIT_LIST_HEAD(&mmu->vma_list);
826+ spin_lock_init(&mmu->ready_lock);
827+
828+ mmu->dmap = alloc_dma_mapping(isp);
829+ if (!mmu->dmap) {
830+ dev_err(dev, "can't alloc dma mapping\n");
831+ return ERR_PTR(-ENOMEM);
832+ }
833+
834+ return mmu;
835+}
836+
837+void ipu6_mmu_cleanup(struct ipu6_mmu *mmu)
838+{
839+ struct ipu6_dma_mapping *dmap = mmu->dmap;
840+
841+ ipu6_mmu_destroy(mmu);
842+ mmu->dmap = NULL;
843+ iova_cache_put();
844+ put_iova_domain(&dmap->iovad);
845+ kfree(dmap);
846+}
drivers/media/pci/intel/ipu6/ipu6-mmu.h+73−0
@@ -0,0 +1,73 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2013--2024 Intel Corporation */
3+
4+#ifndef IPU6_MMU_H
5+#define IPU6_MMU_H
6+
7+#define ISYS_MMID 1
8+#define PSYS_MMID 0
9+
10+#include <linux/list.h>
11+#include <linux/spinlock_types.h>
12+#include <linux/types.h>
13+
14+struct device;
15+struct page;
16+struct ipu6_hw_variants;
17+
18+struct ipu6_mmu_info {
19+ struct device *dev;
20+
21+ u32 *l1_pt;
22+ u32 l1_pt_dma;
23+ u32 **l2_pts;
24+
25+ u32 *dummy_l2_pt;
26+ u32 dummy_l2_pteval;
27+ void *dummy_page;
28+ u32 dummy_page_pteval;
29+
30+ dma_addr_t aperture_start;
31+ dma_addr_t aperture_end;
32+ unsigned long pgsize_bitmap;
33+
34+ spinlock_t lock; /* Serialize access to users */
35+ struct ipu6_dma_mapping *dmap;
36+};
37+
38+struct ipu6_mmu {
39+ struct list_head node;
40+
41+ struct ipu6_mmu_hw *mmu_hw;
42+ unsigned int nr_mmus;
43+ unsigned int mmid;
44+
45+ phys_addr_t pgtbl;
46+ struct device *dev;
47+
48+ struct ipu6_dma_mapping *dmap;
49+ struct list_head vma_list;
50+
51+ struct page *trash_page;
52+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */
53+ dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */
54+
55+ bool ready;
56+ spinlock_t ready_lock; /* Serialize access to bool ready */
57+
58+ void (*tlb_invalidate)(struct ipu6_mmu *mmu);
59+};
60+
61+struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
62+ void __iomem *base, int mmid,
63+ const struct ipu6_hw_variants *hw);
64+void ipu6_mmu_cleanup(struct ipu6_mmu *mmu);
65+int ipu6_mmu_hw_init(struct ipu6_mmu *mmu);
66+void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu);
67+int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
68+ phys_addr_t paddr, size_t size);
69+size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
70+ size_t size);
71+phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
72+ dma_addr_t iova);
73+#endif
drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h+226−0
@@ -0,0 +1,226 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2023--2024 Intel Corporation */
3+
4+#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H
5+#define IPU6_PLATFORM_BUTTRESS_REGS_H
6+
7+#include <linux/bits.h>
8+
9+/* IS_WORKPOINT_REQ */
10+#define IPU6_BUTTRESS_REG_IS_FREQ_CTL 0x34
11+/* PS_WORKPOINT_REQ */
12+#define IPU6_BUTTRESS_REG_PS_FREQ_CTL 0x38
13+
14+/* should be tuned for real silicon */
15+#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO 0x08
16+#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO 0x0a
17+#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO 0x0d
18+
19+#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x10
20+#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO 0x0708
21+
22+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3
23+#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK GENMASK(4, 3)
24+
25+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6
26+#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK GENMASK(7, 6)
27+
28+#define IPU6_BUTTRESS_PWR_STATE_DN_DONE 0x0
29+#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS 0x1
30+#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS 0x2
31+#define IPU6_BUTTRESS_PWR_STATE_UP_DONE 0x3
32+
33+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0 0x270
34+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1 0x274
35+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2 0x278
36+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3 0x27c
37+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4 0x280
38+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5 0x284
39+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6 0x288
40+#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7 0x28c
41+
42+#define BUTTRESS_REG_WDT 0x8
43+#define BUTTRESS_REG_BTRS_CTRL 0xc
44+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0 BIT(0)
45+#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1 BIT(1)
46+#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND GENMASK(9, 8)
47+
48+#define BUTTRESS_REG_FW_RESET_CTL 0x30
49+#define BUTTRESS_FW_RESET_CTL_START BIT(0)
50+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1)
51+
52+#define BUTTRESS_REG_IS_FREQ_CTL 0x34
53+#define BUTTRESS_REG_PS_FREQ_CTL 0x38
54+
55+#define BUTTRESS_FREQ_CTL_START BIT(31)
56+#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL GENMASK(19, 16)
57+#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK GENMASK(15, 8)
58+#define BUTTRESS_FREQ_CTL_RATIO_MASK GENMASK(7, 0)
59+
60+#define BUTTRESS_REG_PWR_STATE 0x5c
61+
62+#define BUTTRESS_PWR_STATE_RESET 0x0
63+#define BUTTRESS_PWR_STATE_PWR_ON_DONE 0x1
64+#define BUTTRESS_PWR_STATE_PWR_RDY 0x3
65+#define BUTTRESS_PWR_STATE_PWR_IDLE 0x4
66+
67+#define BUTTRESS_PWR_STATE_HH_STATUS_MASK GENMASK(12, 11)
68+
69+enum {
70+ BUTTRESS_PWR_STATE_HH_STATE_IDLE,
71+ BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS,
72+ BUTTRESS_PWR_STATE_HH_STATE_DONE,
73+ BUTTRESS_PWR_STATE_HH_STATE_ERR,
74+};
75+
76+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK GENMASK(23, 19)
77+
78+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE 0x0
79+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP 0x1
80+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK 0x2
81+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK 0x3
82+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES 0x4
83+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1 0x5
84+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2 0x6
85+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES 0x7
86+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP 0x8
87+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT 0x9
88+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY 0xa
89+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED 0xb
90+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3 0xc
91+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD 0xd
92+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT 0xe
93+#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0 0xf
94+
95+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK GENMASK(28, 24)
96+
97+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE 0x0
98+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY 0x1
99+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH 0x2
100+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD 0x3
101+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH 0x4
102+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO 0x5
103+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP 0x6
104+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK 0x7
105+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES 0x8
106+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1 0x9
107+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2 0xa
108+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES 0xb
109+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT 0xc
110+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT 0xd
111+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP 0xf
112+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED 0x10
113+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3 0x11
114+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK 0x12
115+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND 0x13
116+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4 0x14
117+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP 0x15
118+#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK 0x16
119+
120+#define BUTTRESS_REG_SECURITY_CTL 0x300
121+#define BUTTRESS_REG_SKU 0x314
122+#define BUTTRESS_REG_SECURITY_TOUCH 0x318
123+#define BUTTRESS_REG_CAMERA_MASK 0x84
124+
125+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16)
126+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0)
127+
128+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0)
129+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1)
130+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3)
131+
132+#define BUTTRESS_REG_FW_SOURCE_BASE_LO 0x78
133+#define BUTTRESS_REG_FW_SOURCE_BASE_HI 0x7C
134+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x80
135+
136+#define BUTTRESS_REG_ISR_STATUS 0x90
137+#define BUTTRESS_REG_ISR_ENABLED_STATUS 0x94
138+#define BUTTRESS_REG_ISR_ENABLE 0x98
139+#define BUTTRESS_REG_ISR_CLEAR 0x9C
140+
141+#define BUTTRESS_ISR_IS_IRQ BIT(0)
142+#define BUTTRESS_ISR_PS_IRQ BIT(1)
143+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE BIT(2)
144+#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH BIT(3)
145+#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING BIT(4)
146+#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING BIT(5)
147+#define BUTTRESS_ISR_CSE_CSR_SET BIT(6)
148+#define BUTTRESS_ISR_ISH_CSR_SET BIT(7)
149+#define BUTTRESS_ISR_SPURIOUS_CMP BIT(8)
150+#define BUTTRESS_ISR_WATCHDOG_EXPIRED BIT(9)
151+#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ BIT(10)
152+#define BUTTRESS_ISR_SAI_VIOLATION BIT(11)
153+#define BUTTRESS_ISR_HW_ASSERTION BIT(12)
154+#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR BIT(13)
155+#define BUTTRESS_ISR_IS_FATAL_MEM_ERR BIT(14)
156+#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR BIT(15)
157+#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR BIT(16)
158+#define BUTTRESS_ISR_PS_FATAL_MEM_ERR BIT(17)
159+#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR BIT(18)
160+#define BUTTRESS_ISR_PS_FAST_THROTTLE BIT(19)
161+#define BUTTRESS_ISR_UFI_ERROR BIT(20)
162+
163+#define BUTTRESS_REG_IU2CSEDB0 0x100
164+
165+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31)
166+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2
167+
168+#define BUTTRESS_REG_IU2CSEDATA0 0x104
169+
170+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1
171+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2
172+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3
173+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16
174+
175+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0)
176+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1)
177+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2)
178+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4)
179+
180+#define BUTTRESS_REG_IU2CSECSR 0x108
181+
182+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0)
183+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1)
184+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2)
185+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3)
186+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4)
187+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5)
188+
189+#define BUTTRESS_REG_CSE2IUDB0 0x304
190+#define BUTTRESS_REG_CSE2IUCSR 0x30C
191+#define BUTTRESS_REG_CSE2IUDATA0 0x308
192+
193+/* 0x20 == NACK, 0xf == unknown command */
194+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20
195+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0)
196+
197+#define BUTTRESS_REG_ISH2IUCSR 0x50
198+#define BUTTRESS_REG_ISH2IUDB0 0x54
199+#define BUTTRESS_REG_ISH2IUDATA0 0x58
200+
201+#define BUTTRESS_REG_IU2ISHDB0 0x10C
202+#define BUTTRESS_REG_IU2ISHDATA0 0x110
203+#define BUTTRESS_REG_IU2ISHDATA1 0x114
204+#define BUTTRESS_REG_IU2ISHCSR 0x118
205+
206+#define BUTTRESS_REG_FABRIC_CMD 0x88
207+
208+#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC BIT(0)
209+#define BUTTRESS_FABRIC_CMD_IS_DRAIN BIT(4)
210+
211+#define BUTTRESS_REG_TSW_CTL 0x120
212+#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8)
213+
214+#define BUTTRESS_REG_TSC_LO 0x164
215+#define BUTTRESS_REG_TSC_HI 0x168
216+
217+#define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
218+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
219+ BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ)
220+
221+#define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
222+ BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \
223+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
224+ BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | \
225+ BUTTRESS_ISR_SAI_VIOLATION)
226+#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */
drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h+172−0
@@ -0,0 +1,172 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2023--2024 Intel Corporation */
3+
4+#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H
5+#define IPU6_PLATFORM_ISYS_CSI2_REG_H
6+
7+#include <linux/bits.h>
8+
9+#define CSI_REG_BASE 0x220000
10+#define CSI_REG_PORT_BASE(id) (CSI_REG_BASE + (id) * 0x1000)
11+
12+/* CSI Port Genral Purpose Registers */
13+#define CSI_REG_PORT_GPREG_SRST 0x0
14+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4
15+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8
16+
17+/*
18+ * Port IRQs mapping events:
19+ * IRQ0 - CSI_FE event
20+ * IRQ1 - CSI_SYNC
21+ * IRQ2 - S2M_SIDS0TO7
22+ * IRQ3 - S2M_SIDS8TO15
23+ */
24+#define CSI_PORT_REG_BASE_IRQ_CSI 0x80
25+#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC 0xA0
26+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7 0xC0
27+#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15 0xE0
28+
29+#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET 0x0
30+#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET 0x4
31+#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET 0x8
32+#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET 0xc
33+#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET 0x10
34+#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET 0x14
35+
36+#define IPU6SE_CSI_RX_ERROR_IRQ_MASK GENMASK(18, 0)
37+#define IPU6_CSI_RX_ERROR_IRQ_MASK GENMASK(19, 0)
38+
39+#define CSI_RX_NUM_ERRORS_IN_IRQ 20
40+#define CSI_RX_NUM_IRQ 32
41+
42+#define IPU_CSI_RX_IRQ_FS_VC(chn) (1 << ((chn) * 2))
43+#define IPU_CSI_RX_IRQ_FE_VC(chn) (2 << ((chn) * 2))
44+
45+/* PPI2CSI */
46+#define CSI_REG_PPI2CSI_ENABLE 0x200
47+#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF 0x204
48+#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK GENMASK(4, 3)
49+#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE 0x208
50+
51+enum CSI_PPI2CSI_CTRL {
52+ CSI_PPI2CSI_DISABLE = 0,
53+ CSI_PPI2CSI_ENABLE = 1,
54+};
55+
56+/* CSI_FE */
57+#define CSI_REG_CSI_FE_ENABLE 0x280
58+#define CSI_REG_CSI_FE_MODE 0x284
59+#define CSI_REG_CSI_FE_MUX_CTRL 0x288
60+#define CSI_REG_CSI_FE_SYNC_CNTR_SEL 0x290
61+
62+enum CSI_FE_ENABLE_TYPE {
63+ CSI_FE_DISABLE = 0,
64+ CSI_FE_ENABLE = 1,
65+};
66+
67+enum CSI_FE_MODE_TYPE {
68+ CSI_FE_DPHY_MODE = 0,
69+ CSI_FE_CPHY_MODE = 1,
70+};
71+
72+enum CSI_FE_INPUT_SELECTOR {
73+ CSI_SENSOR_INPUT = 0,
74+ CSI_MIPIGEN_INPUT = 1,
75+};
76+
77+enum CSI_FE_SYNC_CNTR_SEL_TYPE {
78+ CSI_CNTR_SENSOR_LINE_ID = BIT(0),
79+ CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID,
80+ CSI_CNTR_SENSOR_FRAME_ID = BIT(1),
81+ CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID,
82+};
83+
84+/* CSI HUB General Purpose Registers */
85+#define CSI_REG_HUB_GPREG_SRST (CSI_REG_BASE + 0x18000)
86+#define CSI_REG_HUB_GPREG_SLV_REG_SRST (CSI_REG_BASE + 0x18004)
87+
88+#define CSI_REG_HUB_DRV_ACCESS_PORT(id) (CSI_REG_BASE + 0x18018 + (id) * 4)
89+#define CSI_REG_HUB_FW_ACCESS_PORT_OFS 0x17000
90+#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS 0x16000
91+#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id) \
92+ (CSI_REG_BASE + (ofs) + (id) * 4)
93+
94+enum CSI_PORT_CLK_GATING_SWITCH {
95+ CSI_PORT_CLK_GATING_OFF = 0,
96+ CSI_PORT_CLK_GATING_ON = 1,
97+};
98+
99+#define CSI_REG_BASE_HUB_IRQ 0x18200
100+
101+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238200
102+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238204
103+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238208
104+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23820c
105+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238210
106+#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238214
107+
108+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238220
109+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238224
110+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238228
111+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23822c
112+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238230
113+#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238234
114+
115+/* MTL IPU6V6 irq ctrl0 & ctrl1 */
116+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE 0x238700
117+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK 0x238704
118+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS 0x238708
119+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR 0x23870c
120+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE 0x238710
121+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE 0x238714
122+
123+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE 0x238720
124+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK 0x238724
125+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS 0x238728
126+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR 0x23872c
127+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE 0x238730
128+#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE 0x238734
129+
130+/*
131+ * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits)
132+ * [0] CSI_PORT.IRQ_CTRL0_csi
133+ * [1] CSI_PORT.IRQ_CTRL1_csi_sync
134+ * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7
135+ * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15
136+ */
137+#define IPU6_ISYS_UNISPART_IRQ_CSI2(port) \
138+ (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE))
139+
140+/*
141+ * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3
142+ * sip0 - 0, 1
143+ * sip1 - 2, 3
144+ * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes
145+ * all offset are base from isys base address
146+ */
147+
148+#define CSI2_HUB_GPREG_SIP_SRST(sip) (0x238038 + (sip) * 4)
149+#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip) (0x238050 + (sip) * 4)
150+
151+#define CSI2_HUB_GPREG_DPHY_TIMER_INCR 0x238040
152+#define CSI2_HUB_GPREG_HPLL_FREQ 0x238044
153+#define CSI2_HUB_GPREG_IS_CLK_RATIO 0x238048
154+#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE 0x23804c
155+#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE 0x238058
156+#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL 0x23805c
157+#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL 0x238088
158+#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL 0x2380a4
159+#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL 0x2380d0
160+
161+#define CSI2_SIP_TOP_CSI_RX_BASE(sip) (0x23805c + (sip) * 0x48)
162+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port) (0x23805c + ((port) / 2) * 0x48)
163+#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) (0x238088 + ((port) / 2) * 0x48)
164+
165+/* offset from port base */
166+#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL 0x0
167+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE 0x4
168+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE 0x8
169+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane) (0xc + (lane) * 8)
170+#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane) (0x10 + (lane) * 8)
171+
172+#endif /* IPU6_ISYS_CSI2_REG_H */
drivers/media/pci/intel/ipu6/ipu6-platform-regs.h+179−0
@@ -0,0 +1,179 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+/* Copyright (C) 2018 - 2024 Intel Corporation */
3+
4+#ifndef IPU6_PLATFORM_REGS_H
5+#define IPU6_PLATFORM_REGS_H
6+
7+#include <linux/bits.h>
8+
9+/*
10+ * IPU6 uses uniform address within IPU6, therefore all subsystem registers
11+ * locates in one single space starts from 0 but in different sctions with
12+ * different addresses, the subsystem offsets are defined to 0 as the
13+ * register definition will have the address offset to 0.
14+ */
15+#define IPU6_UNIFIED_OFFSET 0
16+
17+#define IPU6_ISYS_IOMMU0_OFFSET 0x2e0000
18+#define IPU6_ISYS_IOMMU1_OFFSET 0x2e0500
19+#define IPU6_ISYS_IOMMUI_OFFSET 0x2e0a00
20+
21+#define IPU6_PSYS_IOMMU0_OFFSET 0x1b0000
22+#define IPU6_PSYS_IOMMU1_OFFSET 0x1b0700
23+#define IPU6_PSYS_IOMMU1R_OFFSET 0x1b0e00
24+#define IPU6_PSYS_IOMMUI_OFFSET 0x1b1500
25+
26+/* the offset from IOMMU base register */
27+#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET 0x0c
28+#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET 0x4c
29+#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET 0x8c
30+
31+#define IPU6_MMU_INFO_OFFSET 0x8
32+
33+#define IPU6_ISYS_SPC_OFFSET 0x210000
34+
35+#define IPU6SE_PSYS_SPC_OFFSET 0x110000
36+#define IPU6_PSYS_SPC_OFFSET 0x118000
37+
38+#define IPU6_ISYS_DMEM_OFFSET 0x200000
39+#define IPU6_PSYS_DMEM_OFFSET 0x100000
40+
41+#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE 0x27c000
42+#define IPU6_REG_ISYS_UNISPART_IRQ_MASK 0x27c004
43+#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS 0x27c008
44+#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR 0x27c00c
45+#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE 0x27c010
46+#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE 0x27c014
47+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG 0x27c414
48+#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG 0x27c418
49+#define IPU6_ISYS_UNISPART_IRQ_CSI0 BIT(2)
50+#define IPU6_ISYS_UNISPART_IRQ_CSI1 BIT(3)
51+#define IPU6_ISYS_UNISPART_IRQ_SW BIT(22)
52+
53+#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE 0x2b0200
54+#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK 0x2b0204
55+#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS 0x2b0208
56+#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR 0x2b020c
57+#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE 0x2b0210
58+#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE 0x2b0214
59+
60+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE 0x2d2100
61+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK 0x2d2104
62+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS 0x2d2108
63+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR 0x2d210c
64+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE 0x2d2110
65+#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE 0x2d2114
66+
67+/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */
68+#define IPU6_REG_ISYS_CDC_THRESHOLD(i) (0x27c400 + ((i) * 4))
69+
70+#define IPU6_CSI_IRQ_NUM_PER_PIPE 4
71+#define IPU6SE_ISYS_CSI_PORT_NUM 4
72+#define IPU6_ISYS_CSI_PORT_NUM 8
73+
74+#define IPU6_ISYS_CSI_PORT_IRQ(irq_num) BIT(irq_num)
75+
76+/* PKG DIR OFFSET in IMR in secure mode */
77+#define IPU6_PKG_DIR_IMR_OFFSET 0x40
78+
79+#define IPU6_ISYS_REG_SPC_STATUS_CTRL 0x0
80+
81+#define IPU6_ISYS_SPC_STATUS_START BIT(1)
82+#define IPU6_ISYS_SPC_STATUS_RUN BIT(3)
83+#define IPU6_ISYS_SPC_STATUS_READY BIT(5)
84+#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12)
85+#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH BIT(13)
86+
87+#define IPU6_PSYS_REG_SPC_STATUS_CTRL 0x0
88+#define IPU6_PSYS_REG_SPC_START_PC 0x4
89+#define IPU6_PSYS_REG_SPC_ICACHE_BASE 0x10
90+#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER 0x14
91+
92+#define IPU6_PSYS_SPC_STATUS_START BIT(1)
93+#define IPU6_PSYS_SPC_STATUS_RUN BIT(3)
94+#define IPU6_PSYS_SPC_STATUS_READY BIT(5)
95+#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE BIT(12)
96+#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH BIT(13)
97+
98+#define IPU6_PSYS_REG_SPP0_STATUS_CTRL 0x20000
99+
100+#define IPU6_INFO_ENABLE_SNOOP BIT(0)
101+#define IPU6_INFO_DEC_FORCE_FLUSH BIT(1)
102+#define IPU6_INFO_DEC_PASS_THROUGH BIT(2)
103+#define IPU6_INFO_ZLW BIT(3)
104+#define IPU6_INFO_REQUEST_DESTINATION_IOSF BIT(9)
105+#define IPU6_INFO_IMR_BASE BIT(10)
106+#define IPU6_INFO_IMR_DESTINED BIT(11)
107+
108+#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF
109+
110+/*
111+ * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the
112+ * pixel s2m remp ability.Remap here means that s2m rearange the order
113+ * of the pixels in each 4 pixels group.
114+ * For examle, mirroring remping means that if input's 4 first pixels
115+ * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels.
116+ * 0xE4 is from s2m MAS document. It means no remapping.
117+ */
118+#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
119+/*
120+ * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping.
121+ * This remapping is exactly like the stream2mmio remapping.
122+ */
123+#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
124+
125+#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR 0x1ae000
126+#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR 0x1af000
127+#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n) (0x4 + (n) * 0xc)
128+#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n) (0x8 + (n) * 0xc)
129+#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n) (0xc + (n) * 0xc)
130+
131+enum ipu6_device_ab_group1_target_id {
132+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM,
133+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM,
134+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM,
135+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG,
136+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR,
137+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL,
138+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ,
139+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED,
140+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED,
141+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0,
142+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1,
143+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1,
144+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ,
145+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2,
146+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA,
147+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA,
148+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP,
149+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER,
150+ IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB,
151+};
152+
153+enum nci_ab_access_mode {
154+ NCI_AB_ACCESS_MODE_RW, /* read & write */
155+ NCI_AB_ACCESS_MODE_RO, /* read only */
156+ NCI_AB_ACCESS_MODE_WO, /* write only */
157+ NCI_AB_ACCESS_MODE_NA, /* No access at all */
158+};
159+
160+/* IRQ-related registers in PSYS */
161+#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE 0x1aa200
162+#define IPU6_REG_PSYS_GPDEV_IRQ_MASK 0x1aa204
163+#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS 0x1aa208
164+#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR 0x1aa20c
165+#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE 0x1aa210
166+#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE 0x1aa214
167+/* There are 8 FW interrupts, n = 0..7 */
168+#define IPU6_PSYS_GPDEV_FWIRQ0 5
169+#define IPU6_PSYS_GPDEV_FWIRQ1 6
170+#define IPU6_PSYS_GPDEV_FWIRQ2 7
171+#define IPU6_PSYS_GPDEV_FWIRQ3 8
172+#define IPU6_PSYS_GPDEV_FWIRQ4 9
173+#define IPU6_PSYS_GPDEV_FWIRQ5 10
174+#define IPU6_PSYS_GPDEV_FWIRQ6 11
175+#define IPU6_PSYS_GPDEV_FWIRQ7 12
176+#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n) BIT(n)
177+#define IPU6_REG_PSYS_GPDEV_FWIRQ(n) (4 * (n) + 0x1aa100)
178+
179+#endif /* IPU6_PLATFORM_REGS_H */
drivers/media/pci/intel/ipu6/ipu6.c+856−0
@@ -0,0 +1,856 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+/*
3+ * Copyright (C) 2013--2024 Intel Corporation
4+ */
5+
6+#include <linux/bitfield.h>
7+#include <linux/bits.h>
8+#include <linux/dma-mapping.h>
9+#include <linux/err.h>
10+#include <linux/firmware.h>
11+#include <linux/kernel.h>
12+#include <linux/interrupt.h>
13+#include <linux/io.h>
14+#include <linux/list.h>
15+#include <linux/module.h>
16+#include <linux/pci-ats.h>
17+#include <linux/pm_runtime.h>
18+#include <linux/property.h>
19+#include <linux/scatterlist.h>
20+#include <linux/slab.h>
21+#include <linux/types.h>
22+
23+#include <media/ipu-bridge.h>
24+#include <media/ipu6-pci-table.h>
25+
26+#include "ipu6.h"
27+#include "ipu6-bus.h"
28+#include "ipu6-buttress.h"
29+#include "ipu6-cpd.h"
30+#include "ipu6-isys.h"
31+#include "ipu6-mmu.h"
32+#include "ipu6-platform-buttress-regs.h"
33+#include "ipu6-platform-isys-csi2-reg.h"
34+#include "ipu6-platform-regs.h"
35+
36+#define IPU6_PCI_BAR 0
37+
38+struct ipu6_cell_program {
39+ u32 magic_number;
40+
41+ u32 blob_offset;
42+ u32 blob_size;
43+
44+ u32 start[3];
45+
46+ u32 icache_source;
47+ u32 icache_target;
48+ u32 icache_size;
49+
50+ u32 pmem_source;
51+ u32 pmem_target;
52+ u32 pmem_size;
53+
54+ u32 data_source;
55+ u32 data_target;
56+ u32 data_size;
57+
58+ u32 bss_target;
59+ u32 bss_size;
60+
61+ u32 cell_id;
62+ u32 regs_addr;
63+
64+ u32 cell_pmem_data_bus_address;
65+ u32 cell_dmem_data_bus_address;
66+ u32 cell_pmem_control_bus_address;
67+ u32 cell_dmem_control_bus_address;
68+
69+ u32 next;
70+ u32 dummy[2];
71+};
72+
73+static struct ipu6_isys_internal_pdata isys_ipdata = {
74+ .hw_variant = {
75+ .offset = IPU6_UNIFIED_OFFSET,
76+ .nr_mmus = 3,
77+ .mmu_hw = {
78+ {
79+ .offset = IPU6_ISYS_IOMMU0_OFFSET,
80+ .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF,
81+ .nr_l1streams = 16,
82+ .l1_block_sz = {
83+ 3, 8, 2, 2, 2, 2, 2, 2, 1, 1,
84+ 1, 1, 1, 1, 1, 1
85+ },
86+ .nr_l2streams = 16,
87+ .l2_block_sz = {
88+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
89+ 2, 2, 2, 2, 2, 2
90+ },
91+ .insert_read_before_invalidate = false,
92+ .l1_stream_id_reg_offset =
93+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
94+ .l2_stream_id_reg_offset =
95+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
96+ },
97+ {
98+ .offset = IPU6_ISYS_IOMMU1_OFFSET,
99+ .info_bits = 0,
100+ .nr_l1streams = 16,
101+ .l1_block_sz = {
102+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
103+ 2, 2, 2, 1, 1, 4
104+ },
105+ .nr_l2streams = 16,
106+ .l2_block_sz = {
107+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
108+ 2, 2, 2, 2, 2, 2
109+ },
110+ .insert_read_before_invalidate = false,
111+ .l1_stream_id_reg_offset =
112+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
113+ .l2_stream_id_reg_offset =
114+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
115+ },
116+ {
117+ .offset = IPU6_ISYS_IOMMUI_OFFSET,
118+ .info_bits = 0,
119+ .nr_l1streams = 0,
120+ .nr_l2streams = 0,
121+ .insert_read_before_invalidate = false,
122+ },
123+ },
124+ .cdc_fifos = 3,
125+ .cdc_fifo_threshold = {6, 8, 2},
126+ .dmem_offset = IPU6_ISYS_DMEM_OFFSET,
127+ .spc_offset = IPU6_ISYS_SPC_OFFSET,
128+ },
129+ .isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN,
130+};
131+
132+static struct ipu6_psys_internal_pdata psys_ipdata = {
133+ .hw_variant = {
134+ .offset = IPU6_UNIFIED_OFFSET,
135+ .nr_mmus = 4,
136+ .mmu_hw = {
137+ {
138+ .offset = IPU6_PSYS_IOMMU0_OFFSET,
139+ .info_bits =
140+ IPU6_INFO_REQUEST_DESTINATION_IOSF,
141+ .nr_l1streams = 16,
142+ .l1_block_sz = {
143+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
144+ 2, 2, 2, 2, 2, 2
145+ },
146+ .nr_l2streams = 16,
147+ .l2_block_sz = {
148+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
149+ 2, 2, 2, 2, 2, 2
150+ },
151+ .insert_read_before_invalidate = false,
152+ .l1_stream_id_reg_offset =
153+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
154+ .l2_stream_id_reg_offset =
155+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
156+ },
157+ {
158+ .offset = IPU6_PSYS_IOMMU1_OFFSET,
159+ .info_bits = 0,
160+ .nr_l1streams = 32,
161+ .l1_block_sz = {
162+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
163+ 2, 2, 2, 2, 2, 10,
164+ 5, 4, 14, 6, 4, 14, 6, 4, 8,
165+ 4, 2, 1, 1, 1, 1, 14
166+ },
167+ .nr_l2streams = 32,
168+ .l2_block_sz = {
169+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
170+ 2, 2, 2, 2, 2, 2,
171+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
172+ 2, 2, 2, 2, 2, 2
173+ },
174+ .insert_read_before_invalidate = false,
175+ .l1_stream_id_reg_offset =
176+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
177+ .l2_stream_id_reg_offset =
178+ IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET,
179+ },
180+ {
181+ .offset = IPU6_PSYS_IOMMU1R_OFFSET,
182+ .info_bits = 0,
183+ .nr_l1streams = 16,
184+ .l1_block_sz = {
185+ 1, 4, 4, 4, 4, 16, 8, 4, 32,
186+ 16, 16, 2, 2, 2, 1, 12
187+ },
188+ .nr_l2streams = 16,
189+ .l2_block_sz = {
190+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
191+ 2, 2, 2, 2, 2, 2
192+ },

664 more lines aren’t shown here.

drivers/media/pci/intel/ipu6/ipu6.h+342−0

This diff is large: its 343 lines aren’t shown here.

drivers/media/pci/intel/ivsc/mei_csi.c+18−2

This diff is large: its 50 lines aren’t shown here.

drivers/media/pci/mgb4/mgb4_core.c+2−2

This diff is large: its 16 lines aren’t shown here.

drivers/media/pci/mgb4/mgb4_regs.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/pci/ngene/ngene-core.c+3−1

This diff is large: its 11 lines aren’t shown here.

drivers/media/pci/saa7134/saa7134-alsa.c+2−7

This diff is large: its 23 lines aren’t shown here.

drivers/media/pci/solo6x10/solo6x10-core.c+4−12

This diff is large: its 41 lines aren’t shown here.

drivers/media/pci/ttpci/budget-av.c+286−287

This diff is large: its 850 lines aren’t shown here.

drivers/media/pci/ttpci/budget-ci.c+247−248

This diff is large: its 674 lines aren’t shown here.

drivers/media/pci/ttpci/budget-core.c+20−18

This diff is large: its 150 lines aren’t shown here.

drivers/media/pci/ttpci/budget.c+93−80

This diff is large: its 476 lines aren’t shown here.

drivers/media/pci/ttpci/budget.h+13−8

This diff is large: its 52 lines aren’t shown here.

drivers/media/platform/Kconfig+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/Makefile+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/broadcom/Kconfig+23−0

This diff is large: its 24 lines aren’t shown here.

drivers/media/platform/broadcom/Makefile+3−0

This diff is large: its 4 lines aren’t shown here.

drivers/media/platform/broadcom/bcm2835-unicam-regs.h+246−0

This diff is large: its 247 lines aren’t shown here.

drivers/media/platform/broadcom/bcm2835-unicam.c+2739−0

This diff is large: its 2740 lines aren’t shown here.

drivers/media/platform/cadence/cdns-csi2rx.c+14−12

This diff is large: its 54 lines aren’t shown here.

drivers/media/platform/chips-media/wave5/wave5-helper.c+15−2

This diff is large: its 36 lines aren’t shown here.

drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c+12−1

This diff is large: its 27 lines aren’t shown here.

drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c+12−1

This diff is large: its 27 lines aren’t shown here.

drivers/media/platform/chips-media/wave5/wave5-vpu.c+83−42

This diff is large: its 176 lines aren’t shown here.

drivers/media/platform/chips-media/wave5/wave5-vpuapi.h+4−0

This diff is large: its 11 lines aren’t shown here.

drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c+5−5

This diff is large: its 39 lines aren’t shown here.

drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c+3−3

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c+3−3

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c+2−0

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c+11−12

This diff is large: its 59 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h+2−2

This diff is large: its 14 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c+25−17

This diff is large: its 102 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c+6−6

This diff is large: its 54 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h+9−6

This diff is large: its 44 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c+7−7

This diff is large: its 63 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c+3−3

This diff is large: its 21 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h+2−2

This diff is large: its 17 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c+3−1

This diff is large: its 17 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c+3−2

This diff is large: its 15 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c+4−1

This diff is large: its 19 lines aren’t shown here.

drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/nvidia/tegra-vde/h264.c+3−3

This diff is large: its 20 lines aren’t shown here.

drivers/media/platform/nxp/imx-mipi-csis.c+33−1

This diff is large: its 71 lines aren’t shown here.

drivers/media/platform/qcom/camss/Makefile+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c+105−3

This diff is large: its 141 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss-csiphy.c+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss-vfe-17x.crenamed from drivers/media/platform/qcom/camss/camss-vfe-170.c+0−0

File renamed without changes.

drivers/media/platform/qcom/camss/camss-vfe.c+20−5

This diff is large: its 53 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss-video.c+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss.c+307−0

This diff is large: its 327 lines aren’t shown here.

drivers/media/platform/qcom/camss/camss.h+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c+6−3

This diff is large: its 30 lines aren’t shown here.

drivers/media/platform/renesas/rcar-vin/rcar-vin.h+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/st/sti/c8sectpfe/Kconfig+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/st/sti/c8sectpfe/Makefile+5−2

This diff is large: its 12 lines aren’t shown here.

drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c+1−2

This diff is large: its 24 lines aren’t shown here.

drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h+5−0

This diff is large: its 13 lines aren’t shown here.

drivers/media/platform/st/sti/hva/hva-hw.c+1−2

This diff is large: its 10 lines aren’t shown here.

drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c+2−5

This diff is large: its 14 lines aren’t shown here.

drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/platform/ti/davinci/vpif_capture.c+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/ti/davinci/vpif_display.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c+2−3

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/verisilicon/hantro_h1_regs.h+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/media/platform/verisilicon/hantro_v4l2.c+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/radio/radio-shark2.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/rc/gpio-ir-recv.c+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/media/rc/imon.c+6−6

This diff is large: its 20 lines aren’t shown here.

drivers/media/rc/ir-spi.c+22−19

This diff is large: its 110 lines aren’t shown here.

drivers/media/rc/lirc_dev.c+9−9

This diff is large: its 49 lines aren’t shown here.

drivers/media/spi/cxd2880-spi.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/spi/gs1662.c+18−9

This diff is large: its 70 lines aren’t shown here.

drivers/media/test-drivers/vicodec/vicodec-core.c+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/test-drivers/vimc/vimc-capture.c+2−1

This diff is large: its 17 lines aren’t shown here.

drivers/media/test-drivers/visl/visl-video.c+1−0

This diff is large: its 8 lines aren’t shown here.

drivers/media/test-drivers/vivid/vivid-core.c+5−4

This diff is large: its 44 lines aren’t shown here.

drivers/media/test-drivers/vivid/vivid-meta-out.c+0−4

This diff is large: its 18 lines aren’t shown here.

drivers/media/test-drivers/vivid/vivid-touch-cap.c+0−4

This diff is large: its 18 lines aren’t shown here.

drivers/media/tuners/xc5000.c+17−22

This diff is large: its 82 lines aren’t shown here.

drivers/media/usb/as102/as102_usb_drv.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/usb/au0828/au0828-video.c+1−4

This diff is large: its 12 lines aren’t shown here.

drivers/media/usb/b2c2/flexcop-usb.c+10−7

This diff is large: its 52 lines aren’t shown here.

drivers/media/usb/dvb-usb-v2/af9035.c+9−1

This diff is large: its 31 lines aren’t shown here.

drivers/media/usb/dvb-usb-v2/anysee.c+2−2

This diff is large: its 17 lines aren’t shown here.

drivers/media/usb/dvb-usb/dib0700_devices.c+15−3

This diff is large: its 40 lines aren’t shown here.

drivers/media/usb/dvb-usb/dw2102.c+242−220

This diff is large: its 1080 lines aren’t shown here.

drivers/media/usb/go7007/go7007-fw.c+2−2

This diff is large: its 11 lines aren’t shown here.

drivers/media/usb/gspca/cpia1.c+2−4

This diff is large: its 13 lines aren’t shown here.

drivers/media/usb/s2255/s2255drv.c+10−10

This diff is large: its 79 lines aren’t shown here.

drivers/media/usb/siano/smsusb.c+14−6

This diff is large: its 77 lines aren’t shown here.

drivers/media/usb/stk1160/stk1160-video.c+17−13

This diff is large: its 61 lines aren’t shown here.

drivers/media/usb/uvc/uvc_ctrl.c+14−12

This diff is large: its 64 lines aren’t shown here.

drivers/media/usb/uvc/uvc_driver.c+53−1

This diff is large: its 90 lines aren’t shown here.

drivers/media/usb/uvc/uvcvideo.h+2−0

This diff is large: its 9 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-async.c+15−15

This diff is large: its 107 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-common.c+2−0

This diff is large: its 10 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-ctrls-api.c+19−14

This diff is large: its 55 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-ctrls-core.c+11−13

This diff is large: its 67 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-dev.c+6−0

This diff is large: its 29 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-device.c+7−5

This diff is large: its 41 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-i2c.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-ioctl.c+66−7

This diff is large: its 171 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-mem2mem.c+15−0

This diff is large: its 22 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-spi.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/media/v4l2-core/v4l2-subdev.c+85−28

This diff is large: its 208 lines aren’t shown here.

drivers/staging/media/atomisp/Makefile+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/Kconfig+0−15

This diff is large: its 19 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/Makefile+0−5

This diff is large: its 9 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/atomisp-gc2235.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/atomisp-lm3554.c+0−955

This diff is large: its 956 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c+2−2

This diff is large: its 18 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/atomisp-ov2722.c+1−13

This diff is large: its 63 lines aren’t shown here.

drivers/staging/media/atomisp/i2c/ov2722.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/include/linux/atomisp.h+0−81

This diff is large: its 130 lines aren’t shown here.

drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h+1−5

This diff is large: its 18 lines aren’t shown here.

drivers/staging/media/atomisp/include/linux/atomisp_platform.h+1−40

This diff is large: its 75 lines aren’t shown here.

drivers/staging/media/atomisp/include/media/lm3554.h+0−132

This diff is large: its 133 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_cmd.c+125−156

This diff is large: its 442 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_cmd.h+9−4

This diff is large: its 34 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_compat_css20.c+1−30

This diff is large: its 50 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_csi2.c+3−23

This diff is large: its 50 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_fops.c+1−48

This diff is large: its 94 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c+32−91

This diff is large: its 283 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_internal.h+3−16

This diff is large: its 77 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_ioctl.c+40−453

This diff is large: its 728 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_subdev.c+52−50

This diff is large: its 178 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_subdev.h+1−16

This diff is large: its 45 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_tpg.c+0−164

This diff is large: its 165 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_tpg.h+0−39

This diff is large: its 40 lines aren’t shown here.

drivers/staging/media/atomisp/pci/atomisp_v4l2.c+61−122

This diff is large: its 350 lines aren’t shown here.

drivers/staging/media/atomisp/pci/bits.h+2−2

This diff is large: its 12 lines aren’t shown here.

drivers/staging/media/atomisp/pci/defs.h+0−37

This diff is large: its 38 lines aren’t shown here.

drivers/staging/media/atomisp/pci/hive_isp_css_common/host/dma_local.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c+0−38

This diff is large: its 59 lines aren’t shown here.

drivers/staging/media/atomisp/pci/hive_types.h+0−19

This diff is large: its 26 lines aren’t shown here.

drivers/staging/media/atomisp/pci/ia_css.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/pci/ia_css_frame_public.h+0−8

This diff is large: its 29 lines aren’t shown here.

drivers/staging/media/atomisp/pci/ia_css_stream_public.h+0−17

This diff is large: its 45 lines aren’t shown here.

drivers/staging/media/atomisp/pci/ia_css_tpg.h+0−79

This diff is large: its 80 lines aren’t shown here.

drivers/staging/media/atomisp/pci/isp2400_input_system_global.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/pci/isp2400_input_system_public.h+0−15

This diff is large: its 22 lines aren’t shown here.

drivers/staging/media/atomisp/pci/isp2401_input_system_global.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c+2−18

This diff is large: its 41 lines aren’t shown here.

drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c+0−11

This diff is large: its 18 lines aren’t shown here.

drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c+1−27

This diff is large: its 56 lines aren’t shown here.

drivers/staging/media/atomisp/pci/sh_css.c+9−129

This diff is large: its 238 lines aren’t shown here.

drivers/staging/media/atomisp/pci/sh_css_internal.h+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/atomisp/pci/sh_css_mipi.c+1−1

This diff is large: its 9 lines aren’t shown here.

drivers/staging/media/atomisp/pci/sh_css_sp.c+61−66

This diff is large: its 270 lines aren’t shown here.

drivers/staging/media/atomisp/pci/sh_css_sp.h+0−7

This diff is large: its 14 lines aren’t shown here.

drivers/staging/media/atomisp/pci/system_global.h+0−12

This diff is large: its 19 lines aren’t shown here.

drivers/staging/media/imx/Kconfig+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/ipu3/ipu3-css-fw.c+3−1

This diff is large: its 11 lines aren’t shown here.

drivers/staging/media/ipu3/ipu3-css-fw.h+2−0

This diff is large: its 9 lines aren’t shown here.

drivers/staging/media/ipu3/ipu3-css.c+0−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/ipu3/ipu3.c+7−3

This diff is large: its 25 lines aren’t shown here.

drivers/staging/media/max96712/max96712.c+1−1

This diff is large: its 8 lines aren’t shown here.

drivers/staging/media/starfive/camss/stf-camss.c+8−4

This diff is large: its 40 lines aren’t shown here.

drivers/staging/media/starfive/camss/stf-isp.c+2−8

This diff is large: its 31 lines aren’t shown here.

drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c+1−2

This diff is large: its 10 lines aren’t shown here.

drivers/staging/media/tegra-video/tegra20.c+3−7

This diff is large: its 24 lines aren’t shown here.

drivers/staging/media/tegra-video/vi.c+6−6

This diff is large: its 46 lines aren’t shown here.

include/media/cec.h+3−1

This diff is large: its 25 lines aren’t shown here.

include/media/ipu6-pci-table.h+28−0

This diff is large: its 29 lines aren’t shown here.

include/media/media-device.h+6−0

This diff is large: its 20 lines aren’t shown here.

include/media/v4l2-async.h+3−1

This diff is large: its 11 lines aren’t shown here.

include/media/v4l2-device.h+5−2

This diff is large: its 14 lines aren’t shown here.

include/media/v4l2-ioctl.h+4−0

This diff is large: its 18 lines aren’t shown here.

include/media/v4l2-mem2mem.h+2−0

This diff is large: its 9 lines aren’t shown here.

include/media/v4l2-subdev.h+56−14

This diff is large: its 118 lines aren’t shown here.

include/media/videobuf2-core.h+42−10

This diff is large: its 145 lines aren’t shown here.

include/media/videobuf2-v4l2.h+2−0

This diff is large: its 9 lines aren’t shown here.

include/uapi/linux/dvb/frontend.h+1−1

This diff is large: its 9 lines aren’t shown here.

include/uapi/linux/media-bus-format.h+9−0

This diff is large: its 14 lines aren’t shown here.

include/uapi/linux/v4l2-mediabus.h+12−6

This diff is large: its 25 lines aren’t shown here.

include/uapi/linux/v4l2-subdev.h+11−3

This diff is large: its 34 lines aren’t shown here.

include/uapi/linux/videodev2.h+44−1

This diff is large: its 105 lines aren’t shown here.