imx8qxp-pixel-combiner.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright 2020 NXP
  4. */
  5. #include <linux/bitfield.h>
  6. #include <linux/clk.h>
  7. #include <linux/delay.h>
  8. #include <linux/io.h>
  9. #include <linux/media-bus-format.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_graph.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/pm_runtime.h>
  15. #include <drm/drm_atomic_state_helper.h>
  16. #include <drm/drm_bridge.h>
  17. #include <drm/drm_print.h>
  18. #define PC_CTRL_REG 0x0
  19. #define PC_COMBINE_ENABLE BIT(0)
  20. #define PC_DISP_BYPASS(n) BIT(1 + 21 * (n))
  21. #define PC_DISP_HSYNC_POLARITY(n) BIT(2 + 11 * (n))
  22. #define PC_DISP_HSYNC_POLARITY_POS(n) DISP_HSYNC_POLARITY(n)
  23. #define PC_DISP_VSYNC_POLARITY(n) BIT(3 + 11 * (n))
  24. #define PC_DISP_VSYNC_POLARITY_POS(n) DISP_VSYNC_POLARITY(n)
  25. #define PC_DISP_DVALID_POLARITY(n) BIT(4 + 11 * (n))
  26. #define PC_DISP_DVALID_POLARITY_POS(n) DISP_DVALID_POLARITY(n)
  27. #define PC_VSYNC_MASK_ENABLE BIT(5)
  28. #define PC_SKIP_MODE BIT(6)
  29. #define PC_SKIP_NUMBER_MASK GENMASK(12, 7)
  30. #define PC_SKIP_NUMBER(n) FIELD_PREP(PC_SKIP_NUMBER_MASK, (n))
  31. #define PC_DISP0_PIX_DATA_FORMAT_MASK GENMASK(18, 16)
  32. #define PC_DISP0_PIX_DATA_FORMAT(fmt) \
  33. FIELD_PREP(PC_DISP0_PIX_DATA_FORMAT_MASK, (fmt))
  34. #define PC_DISP1_PIX_DATA_FORMAT_MASK GENMASK(21, 19)
  35. #define PC_DISP1_PIX_DATA_FORMAT(fmt) \
  36. FIELD_PREP(PC_DISP1_PIX_DATA_FORMAT_MASK, (fmt))
  37. #define PC_SW_RESET_REG 0x20
  38. #define PC_SW_RESET_N BIT(0)
  39. #define PC_DISP_SW_RESET_N(n) BIT(1 + (n))
  40. #define PC_FULL_RESET_N (PC_SW_RESET_N | \
  41. PC_DISP_SW_RESET_N(0) | \
  42. PC_DISP_SW_RESET_N(1))
  43. #define PC_REG_SET 0x4
  44. #define PC_REG_CLR 0x8
  45. #define DRIVER_NAME "imx8qxp-pixel-combiner"
  46. enum imx8qxp_pc_pix_data_format {
  47. RGB,
  48. YUV444,
  49. YUV422,
  50. SPLIT_RGB,
  51. };
  52. struct imx8qxp_pc_channel {
  53. struct drm_bridge bridge;
  54. struct drm_bridge *next_bridge;
  55. struct imx8qxp_pc *pc;
  56. unsigned int stream_id;
  57. bool is_available;
  58. };
  59. struct imx8qxp_pc {
  60. struct device *dev;
  61. struct imx8qxp_pc_channel ch[2];
  62. struct clk *clk_apb;
  63. void __iomem *base;
  64. };
  65. static inline u32 imx8qxp_pc_read(struct imx8qxp_pc *pc, unsigned int offset)
  66. {
  67. return readl(pc->base + offset);
  68. }
  69. static inline void
  70. imx8qxp_pc_write(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
  71. {
  72. writel(value, pc->base + offset);
  73. }
  74. static inline void
  75. imx8qxp_pc_write_set(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
  76. {
  77. imx8qxp_pc_write(pc, offset + PC_REG_SET, value);
  78. }
  79. static inline void
  80. imx8qxp_pc_write_clr(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
  81. {
  82. imx8qxp_pc_write(pc, offset + PC_REG_CLR, value);
  83. }
  84. static enum drm_mode_status
  85. imx8qxp_pc_bridge_mode_valid(struct drm_bridge *bridge,
  86. const struct drm_display_info *info,
  87. const struct drm_display_mode *mode)
  88. {
  89. if (mode->hdisplay > 2560)
  90. return MODE_BAD_HVALUE;
  91. return MODE_OK;
  92. }
  93. static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,
  94. enum drm_bridge_attach_flags flags)
  95. {
  96. struct imx8qxp_pc_channel *ch = bridge->driver_private;
  97. struct imx8qxp_pc *pc = ch->pc;
  98. if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
  99. DRM_DEV_ERROR(pc->dev,
  100. "do not support creating a drm_connector\n");
  101. return -EINVAL;
  102. }
  103. if (!bridge->encoder) {
  104. DRM_DEV_ERROR(pc->dev, "missing encoder\n");
  105. return -ENODEV;
  106. }
  107. return drm_bridge_attach(bridge->encoder,
  108. ch->next_bridge, bridge,
  109. DRM_BRIDGE_ATTACH_NO_CONNECTOR);
  110. }
  111. static void
  112. imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,
  113. const struct drm_display_mode *mode,
  114. const struct drm_display_mode *adjusted_mode)
  115. {
  116. struct imx8qxp_pc_channel *ch = bridge->driver_private;
  117. struct imx8qxp_pc *pc = ch->pc;
  118. u32 val;
  119. int ret;
  120. ret = pm_runtime_get_sync(pc->dev);
  121. if (ret < 0)
  122. DRM_DEV_ERROR(pc->dev,
  123. "failed to get runtime PM sync: %d\n", ret);
  124. ret = clk_prepare_enable(pc->clk_apb);
  125. if (ret)
  126. DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
  127. __func__, ret);
  128. /* HSYNC to pixel link is active low. */
  129. imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
  130. PC_DISP_HSYNC_POLARITY(ch->stream_id));
  131. /* VSYNC to pixel link is active low. */
  132. imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
  133. PC_DISP_VSYNC_POLARITY(ch->stream_id));
  134. /* Data enable to pixel link is active high. */
  135. imx8qxp_pc_write_set(pc, PC_CTRL_REG,
  136. PC_DISP_DVALID_POLARITY(ch->stream_id));
  137. /* Mask the first frame output which may be incomplete. */
  138. imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);
  139. /* Only support RGB currently. */
  140. val = imx8qxp_pc_read(pc, PC_CTRL_REG);
  141. if (ch->stream_id == 0) {
  142. val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;
  143. val |= PC_DISP0_PIX_DATA_FORMAT(RGB);
  144. } else {
  145. val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;
  146. val |= PC_DISP1_PIX_DATA_FORMAT(RGB);
  147. }
  148. imx8qxp_pc_write(pc, PC_CTRL_REG, val);
  149. /* Only support bypass mode currently. */
  150. imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));
  151. clk_disable_unprepare(pc->clk_apb);
  152. }
  153. static void
  154. imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,
  155. struct drm_bridge_state *old_bridge_state)
  156. {
  157. struct imx8qxp_pc_channel *ch = bridge->driver_private;
  158. struct imx8qxp_pc *pc = ch->pc;
  159. int ret;
  160. ret = pm_runtime_put(pc->dev);
  161. if (ret < 0)
  162. DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);
  163. }
  164. static const u32 imx8qxp_pc_bus_output_fmts[] = {
  165. MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
  166. MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
  167. };
  168. static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)
  169. {
  170. int i;
  171. for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {
  172. if (imx8qxp_pc_bus_output_fmts[i] == fmt)
  173. return true;
  174. }
  175. return false;
  176. }
  177. static u32 *
  178. imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
  179. struct drm_bridge_state *bridge_state,
  180. struct drm_crtc_state *crtc_state,
  181. struct drm_connector_state *conn_state,
  182. u32 output_fmt,
  183. unsigned int *num_input_fmts)
  184. {
  185. u32 *input_fmts;
  186. if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))
  187. return NULL;
  188. *num_input_fmts = 1;
  189. input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
  190. if (!input_fmts)
  191. return NULL;
  192. switch (output_fmt) {
  193. case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:
  194. input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;
  195. break;
  196. case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:
  197. input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;
  198. break;
  199. default:
  200. kfree(input_fmts);
  201. input_fmts = NULL;
  202. break;
  203. }
  204. return input_fmts;
  205. }
  206. static u32 *
  207. imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
  208. struct drm_bridge_state *bridge_state,
  209. struct drm_crtc_state *crtc_state,
  210. struct drm_connector_state *conn_state,
  211. unsigned int *num_output_fmts)
  212. {
  213. *num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);
  214. return kmemdup(imx8qxp_pc_bus_output_fmts,
  215. sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);
  216. }
  217. static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
  218. .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
  219. .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
  220. .atomic_reset = drm_atomic_helper_bridge_reset,
  221. .mode_valid = imx8qxp_pc_bridge_mode_valid,
  222. .attach = imx8qxp_pc_bridge_attach,
  223. .mode_set = imx8qxp_pc_bridge_mode_set,
  224. .atomic_disable = imx8qxp_pc_bridge_atomic_disable,
  225. .atomic_get_input_bus_fmts =
  226. imx8qxp_pc_bridge_atomic_get_input_bus_fmts,
  227. .atomic_get_output_bus_fmts =
  228. imx8qxp_pc_bridge_atomic_get_output_bus_fmts,
  229. };
  230. static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
  231. {
  232. struct imx8qxp_pc *pc;
  233. struct imx8qxp_pc_channel *ch;
  234. struct device *dev = &pdev->dev;
  235. struct device_node *np = dev->of_node;
  236. struct device_node *child, *remote;
  237. u32 i;
  238. int ret;
  239. pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
  240. if (!pc)
  241. return -ENOMEM;
  242. pc->base = devm_platform_ioremap_resource(pdev, 0);
  243. if (IS_ERR(pc->base))
  244. return PTR_ERR(pc->base);
  245. pc->dev = dev;
  246. pc->clk_apb = devm_clk_get(dev, "apb");
  247. if (IS_ERR(pc->clk_apb)) {
  248. ret = PTR_ERR(pc->clk_apb);
  249. if (ret != -EPROBE_DEFER)
  250. DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);
  251. return ret;
  252. }
  253. platform_set_drvdata(pdev, pc);
  254. pm_runtime_enable(dev);
  255. for_each_available_child_of_node(np, child) {
  256. ret = of_property_read_u32(child, "reg", &i);
  257. if (ret || i > 1) {
  258. ret = -EINVAL;
  259. DRM_DEV_ERROR(dev,
  260. "invalid channel(%u) node address\n", i);
  261. goto free_child;
  262. }
  263. ch = &pc->ch[i];
  264. ch->pc = pc;
  265. ch->stream_id = i;
  266. remote = of_graph_get_remote_node(child, 1, 0);
  267. if (!remote) {
  268. ret = -ENODEV;
  269. DRM_DEV_ERROR(dev,
  270. "channel%u failed to get port1's remote node: %d\n",
  271. i, ret);
  272. goto free_child;
  273. }
  274. ch->next_bridge = of_drm_find_bridge(remote);
  275. if (!ch->next_bridge) {
  276. of_node_put(remote);
  277. ret = -EPROBE_DEFER;
  278. DRM_DEV_DEBUG_DRIVER(dev,
  279. "channel%u failed to find next bridge: %d\n",
  280. i, ret);
  281. goto free_child;
  282. }
  283. of_node_put(remote);
  284. ch->bridge.driver_private = ch;
  285. ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;
  286. ch->bridge.of_node = child;
  287. ch->is_available = true;
  288. drm_bridge_add(&ch->bridge);
  289. }
  290. return 0;
  291. free_child:
  292. of_node_put(child);
  293. if (i == 1 && pc->ch[0].next_bridge)
  294. drm_bridge_remove(&pc->ch[0].bridge);
  295. pm_runtime_disable(dev);
  296. return ret;
  297. }
  298. static int imx8qxp_pc_bridge_remove(struct platform_device *pdev)
  299. {
  300. struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
  301. struct imx8qxp_pc_channel *ch;
  302. int i;
  303. for (i = 0; i < 2; i++) {
  304. ch = &pc->ch[i];
  305. if (!ch->is_available)
  306. continue;
  307. drm_bridge_remove(&ch->bridge);
  308. ch->is_available = false;
  309. }
  310. pm_runtime_disable(&pdev->dev);
  311. return 0;
  312. }
  313. static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
  314. {
  315. struct platform_device *pdev = to_platform_device(dev);
  316. struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
  317. int ret;
  318. ret = clk_prepare_enable(pc->clk_apb);
  319. if (ret)
  320. DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
  321. __func__, ret);
  322. /* Disable pixel combiner by full reset. */
  323. imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
  324. clk_disable_unprepare(pc->clk_apb);
  325. /* Ensure the reset takes effect. */
  326. usleep_range(10, 20);
  327. return ret;
  328. }
  329. static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
  330. {
  331. struct platform_device *pdev = to_platform_device(dev);
  332. struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
  333. int ret;
  334. ret = clk_prepare_enable(pc->clk_apb);
  335. if (ret) {
  336. DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
  337. __func__, ret);
  338. return ret;
  339. }
  340. /* out of reset */
  341. imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
  342. clk_disable_unprepare(pc->clk_apb);
  343. return ret;
  344. }
  345. static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
  346. SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,
  347. imx8qxp_pc_runtime_resume, NULL)
  348. };
  349. static const struct of_device_id imx8qxp_pc_dt_ids[] = {
  350. { .compatible = "fsl,imx8qm-pixel-combiner", },
  351. { .compatible = "fsl,imx8qxp-pixel-combiner", },
  352. { /* sentinel */ }
  353. };
  354. MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
  355. static struct platform_driver imx8qxp_pc_bridge_driver = {
  356. .probe = imx8qxp_pc_bridge_probe,
  357. .remove = imx8qxp_pc_bridge_remove,
  358. .driver = {
  359. .pm = &imx8qxp_pc_pm_ops,
  360. .name = DRIVER_NAME,
  361. .of_match_table = imx8qxp_pc_dt_ids,
  362. },
  363. };
  364. module_platform_driver(imx8qxp_pc_bridge_driver);
  365. MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
  366. MODULE_AUTHOR("Liu Ying <[email protected]>");
  367. MODULE_LICENSE("GPL v2");
  368. MODULE_ALIAS("platform:" DRIVER_NAME);