pl111_versatile.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Versatile family (ARM reference designs) handling for the PL11x.
  4. * This is based on code and know-how in the previous frame buffer
  5. * driver in drivers/video/fbdev/amba-clcd.c:
  6. * Copyright (C) 2001 ARM Limited, by David A Rusling
  7. * Updated to 2.5 by Deep Blue Solutions Ltd.
  8. * Major contributions and discoveries by Russell King.
  9. */
  10. #include <linux/bitops.h>
  11. #include <linux/device.h>
  12. #include <linux/mfd/syscon.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/of_platform.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/regmap.h>
  18. #include <linux/vexpress.h>
  19. #include <drm/drm_fourcc.h>
  20. #include "pl111_versatile.h"
  21. #include "pl111_drm.h"
  22. static struct regmap *versatile_syscon_map;
  23. /*
  24. * We detect the different syscon types from the compatible strings.
  25. */
  26. enum versatile_clcd {
  27. INTEGRATOR_IMPD1,
  28. INTEGRATOR_CLCD_CM,
  29. VERSATILE_CLCD,
  30. REALVIEW_CLCD_EB,
  31. REALVIEW_CLCD_PB1176,
  32. REALVIEW_CLCD_PB11MP,
  33. REALVIEW_CLCD_PBA8,
  34. REALVIEW_CLCD_PBX,
  35. VEXPRESS_CLCD_V2M,
  36. };
  37. static const struct of_device_id versatile_clcd_of_match[] = {
  38. {
  39. .compatible = "arm,core-module-integrator",
  40. .data = (void *)INTEGRATOR_CLCD_CM,
  41. },
  42. {
  43. .compatible = "arm,versatile-sysreg",
  44. .data = (void *)VERSATILE_CLCD,
  45. },
  46. {
  47. .compatible = "arm,realview-eb-syscon",
  48. .data = (void *)REALVIEW_CLCD_EB,
  49. },
  50. {
  51. .compatible = "arm,realview-pb1176-syscon",
  52. .data = (void *)REALVIEW_CLCD_PB1176,
  53. },
  54. {
  55. .compatible = "arm,realview-pb11mp-syscon",
  56. .data = (void *)REALVIEW_CLCD_PB11MP,
  57. },
  58. {
  59. .compatible = "arm,realview-pba8-syscon",
  60. .data = (void *)REALVIEW_CLCD_PBA8,
  61. },
  62. {
  63. .compatible = "arm,realview-pbx-syscon",
  64. .data = (void *)REALVIEW_CLCD_PBX,
  65. },
  66. {
  67. .compatible = "arm,vexpress-muxfpga",
  68. .data = (void *)VEXPRESS_CLCD_V2M,
  69. },
  70. {},
  71. };
  72. static const struct of_device_id impd1_clcd_of_match[] = {
  73. {
  74. .compatible = "arm,im-pd1-syscon",
  75. .data = (void *)INTEGRATOR_IMPD1,
  76. },
  77. {},
  78. };
  79. /*
  80. * Core module CLCD control on the Integrator/CP, bits
  81. * 8 thru 19 of the CM_CONTROL register controls a bunch
  82. * of CLCD settings.
  83. */
  84. #define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
  85. #define INTEGRATOR_CLCD_LCDBIASEN BIT(8)
  86. #define INTEGRATOR_CLCD_LCDBIASUP BIT(9)
  87. #define INTEGRATOR_CLCD_LCDBIASDN BIT(10)
  88. /* Bits 11,12,13 controls the LCD or VGA bridge type */
  89. #define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11)
  90. #define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12))
  91. #define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13)
  92. #define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13))
  93. #define INTEGRATOR_CLCD_LCD0_EN BIT(14)
  94. #define INTEGRATOR_CLCD_LCD1_EN BIT(15)
  95. /* R/L flip on Sharp */
  96. #define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16)
  97. /* U/D flip on Sharp */
  98. #define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17)
  99. /* No connection on Sharp */
  100. #define INTEGRATOR_CLCD_LCD_STATIC BIT(18)
  101. /* 0 = 24bit VGA, 1 = 18bit VGA */
  102. #define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19)
  103. #define INTEGRATOR_CLCD_MASK GENMASK(19, 8)
  104. static void pl111_integrator_enable(struct drm_device *drm, u32 format)
  105. {
  106. u32 val;
  107. dev_info(drm->dev, "enable Integrator CLCD connectors\n");
  108. /* FIXME: really needed? */
  109. val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
  110. INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
  111. switch (format) {
  112. case DRM_FORMAT_XBGR8888:
  113. case DRM_FORMAT_XRGB8888:
  114. /* 24bit formats */
  115. val |= INTEGRATOR_CLCD_LCDMUX_VGA24;
  116. break;
  117. case DRM_FORMAT_XBGR1555:
  118. case DRM_FORMAT_XRGB1555:
  119. /* Pseudocolor, RGB555, BGR555 */
  120. val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
  121. break;
  122. default:
  123. dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n",
  124. format);
  125. break;
  126. }
  127. regmap_update_bits(versatile_syscon_map,
  128. INTEGRATOR_HDR_CTRL_OFFSET,
  129. INTEGRATOR_CLCD_MASK,
  130. val);
  131. }
  132. #define IMPD1_CTRL_OFFSET 0x18
  133. #define IMPD1_CTRL_DISP_LCD (0 << 0)
  134. #define IMPD1_CTRL_DISP_VGA (1 << 0)
  135. #define IMPD1_CTRL_DISP_LCD1 (2 << 0)
  136. #define IMPD1_CTRL_DISP_ENABLE (1 << 2)
  137. #define IMPD1_CTRL_DISP_MASK (7 << 0)
  138. static void pl111_impd1_enable(struct drm_device *drm, u32 format)
  139. {
  140. u32 val;
  141. dev_info(drm->dev, "enable IM-PD1 CLCD connectors\n");
  142. val = IMPD1_CTRL_DISP_VGA | IMPD1_CTRL_DISP_ENABLE;
  143. regmap_update_bits(versatile_syscon_map,
  144. IMPD1_CTRL_OFFSET,
  145. IMPD1_CTRL_DISP_MASK,
  146. val);
  147. }
  148. static void pl111_impd1_disable(struct drm_device *drm)
  149. {
  150. dev_info(drm->dev, "disable IM-PD1 CLCD connectors\n");
  151. regmap_update_bits(versatile_syscon_map,
  152. IMPD1_CTRL_OFFSET,
  153. IMPD1_CTRL_DISP_MASK,
  154. 0);
  155. }
  156. /*
  157. * This configuration register in the Versatile and RealView
  158. * family is uniformly present but appears more and more
  159. * unutilized starting with the RealView series.
  160. */
  161. #define SYS_CLCD 0x50
  162. #define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1))
  163. #define SYS_CLCD_MODE_888 0
  164. #define SYS_CLCD_MODE_5551 BIT(0)
  165. #define SYS_CLCD_MODE_565_R_LSB BIT(1)
  166. #define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1))
  167. #define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5))
  168. #define SYS_CLCD_NLCDIOON BIT(2)
  169. #define SYS_CLCD_VDDPOSSWITCH BIT(3)
  170. #define SYS_CLCD_PWR3V5SWITCH BIT(4)
  171. #define SYS_CLCD_VDDNEGSWITCH BIT(5)
  172. static void pl111_versatile_disable(struct drm_device *drm)
  173. {
  174. dev_info(drm->dev, "disable Versatile CLCD connectors\n");
  175. regmap_update_bits(versatile_syscon_map,
  176. SYS_CLCD,
  177. SYS_CLCD_CONNECTOR_MASK,
  178. 0);
  179. }
  180. static void pl111_versatile_enable(struct drm_device *drm, u32 format)
  181. {
  182. u32 val = 0;
  183. dev_info(drm->dev, "enable Versatile CLCD connectors\n");
  184. switch (format) {
  185. case DRM_FORMAT_ABGR8888:
  186. case DRM_FORMAT_XBGR8888:
  187. case DRM_FORMAT_ARGB8888:
  188. case DRM_FORMAT_XRGB8888:
  189. val |= SYS_CLCD_MODE_888;
  190. break;
  191. case DRM_FORMAT_BGR565:
  192. val |= SYS_CLCD_MODE_565_R_LSB;
  193. break;
  194. case DRM_FORMAT_RGB565:
  195. val |= SYS_CLCD_MODE_565_B_LSB;
  196. break;
  197. case DRM_FORMAT_ABGR1555:
  198. case DRM_FORMAT_XBGR1555:
  199. case DRM_FORMAT_ARGB1555:
  200. case DRM_FORMAT_XRGB1555:
  201. val |= SYS_CLCD_MODE_5551;
  202. break;
  203. default:
  204. dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n",
  205. format);
  206. break;
  207. }
  208. /* Set up the MUX */
  209. regmap_update_bits(versatile_syscon_map,
  210. SYS_CLCD,
  211. SYS_CLCD_MODE_MASK,
  212. val);
  213. /* Then enable the display */
  214. regmap_update_bits(versatile_syscon_map,
  215. SYS_CLCD,
  216. SYS_CLCD_CONNECTOR_MASK,
  217. SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
  218. }
  219. static void pl111_realview_clcd_disable(struct drm_device *drm)
  220. {
  221. dev_info(drm->dev, "disable RealView CLCD connectors\n");
  222. regmap_update_bits(versatile_syscon_map,
  223. SYS_CLCD,
  224. SYS_CLCD_CONNECTOR_MASK,
  225. 0);
  226. }
  227. static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format)
  228. {
  229. dev_info(drm->dev, "enable RealView CLCD connectors\n");
  230. regmap_update_bits(versatile_syscon_map,
  231. SYS_CLCD,
  232. SYS_CLCD_CONNECTOR_MASK,
  233. SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
  234. }
  235. /* PL110 pixel formats for Integrator, vanilla PL110 */
  236. static const u32 pl110_integrator_pixel_formats[] = {
  237. DRM_FORMAT_ABGR8888,
  238. DRM_FORMAT_XBGR8888,
  239. DRM_FORMAT_ARGB8888,
  240. DRM_FORMAT_XRGB8888,
  241. DRM_FORMAT_ABGR1555,
  242. DRM_FORMAT_XBGR1555,
  243. DRM_FORMAT_ARGB1555,
  244. DRM_FORMAT_XRGB1555,
  245. };
  246. /* Extended PL110 pixel formats for Integrator and Versatile */
  247. static const u32 pl110_versatile_pixel_formats[] = {
  248. DRM_FORMAT_ABGR8888,
  249. DRM_FORMAT_XBGR8888,
  250. DRM_FORMAT_ARGB8888,
  251. DRM_FORMAT_XRGB8888,
  252. DRM_FORMAT_BGR565, /* Uses external PLD */
  253. DRM_FORMAT_RGB565, /* Uses external PLD */
  254. DRM_FORMAT_ABGR1555,
  255. DRM_FORMAT_XBGR1555,
  256. DRM_FORMAT_ARGB1555,
  257. DRM_FORMAT_XRGB1555,
  258. };
  259. static const u32 pl111_realview_pixel_formats[] = {
  260. DRM_FORMAT_ABGR8888,
  261. DRM_FORMAT_XBGR8888,
  262. DRM_FORMAT_ARGB8888,
  263. DRM_FORMAT_XRGB8888,
  264. DRM_FORMAT_BGR565,
  265. DRM_FORMAT_RGB565,
  266. DRM_FORMAT_ABGR1555,
  267. DRM_FORMAT_XBGR1555,
  268. DRM_FORMAT_ARGB1555,
  269. DRM_FORMAT_XRGB1555,
  270. DRM_FORMAT_ABGR4444,
  271. DRM_FORMAT_XBGR4444,
  272. DRM_FORMAT_ARGB4444,
  273. DRM_FORMAT_XRGB4444,
  274. };
  275. /*
  276. * The Integrator variant is a PL110 with a bunch of broken, or not
  277. * yet implemented features
  278. */
  279. static const struct pl111_variant_data pl110_integrator = {
  280. .name = "PL110 Integrator",
  281. .is_pl110 = true,
  282. .broken_clockdivider = true,
  283. .broken_vblank = true,
  284. .formats = pl110_integrator_pixel_formats,
  285. .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats),
  286. .fb_depth = 16,
  287. };
  288. /*
  289. * The IM-PD1 variant is a PL110 with a bunch of broken, or not
  290. * yet implemented features
  291. */
  292. static const struct pl111_variant_data pl110_impd1 = {
  293. .name = "PL110 IM-PD1",
  294. .is_pl110 = true,
  295. .broken_clockdivider = true,
  296. .broken_vblank = true,
  297. .formats = pl110_integrator_pixel_formats,
  298. .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats),
  299. .fb_depth = 15,
  300. };
  301. /*
  302. * This is the in-between PL110 variant found in the ARM Versatile,
  303. * supporting RGB565/BGR565
  304. */
  305. static const struct pl111_variant_data pl110_versatile = {
  306. .name = "PL110 Versatile",
  307. .is_pl110 = true,
  308. .external_bgr = true,
  309. .formats = pl110_versatile_pixel_formats,
  310. .nformats = ARRAY_SIZE(pl110_versatile_pixel_formats),
  311. .fb_depth = 16,
  312. };
  313. /*
  314. * RealView PL111 variant, the only real difference from the vanilla
  315. * PL111 is that we select 16bpp framebuffer by default to be able
  316. * to get 1024x768 without saturating the memory bus.
  317. */
  318. static const struct pl111_variant_data pl111_realview = {
  319. .name = "PL111 RealView",
  320. .formats = pl111_realview_pixel_formats,
  321. .nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
  322. .fb_depth = 16,
  323. };
  324. /*
  325. * Versatile Express PL111 variant, again we just push the maximum
  326. * BPP to 16 to be able to get 1024x768 without saturating the memory
  327. * bus. The clockdivider also seems broken on the Versatile Express.
  328. */
  329. static const struct pl111_variant_data pl111_vexpress = {
  330. .name = "PL111 Versatile Express",
  331. .formats = pl111_realview_pixel_formats,
  332. .nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
  333. .fb_depth = 16,
  334. .broken_clockdivider = true,
  335. };
  336. #define VEXPRESS_FPGAMUX_MOTHERBOARD 0x00
  337. #define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1 0x01
  338. #define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2 0x02
  339. static int pl111_vexpress_clcd_init(struct device *dev, struct device_node *np,
  340. struct pl111_drm_dev_private *priv)
  341. {
  342. struct platform_device *pdev;
  343. struct device_node *root;
  344. struct device_node *child;
  345. struct device_node *ct_clcd = NULL;
  346. struct regmap *map;
  347. bool has_coretile_clcd = false;
  348. bool has_coretile_hdlcd = false;
  349. bool mux_motherboard = true;
  350. u32 val;
  351. int ret;
  352. if (!IS_ENABLED(CONFIG_VEXPRESS_CONFIG))
  353. return -ENODEV;
  354. /*
  355. * Check if we have a CLCD or HDLCD on the core tile by checking if a
  356. * CLCD or HDLCD is available in the root of the device tree.
  357. */
  358. root = of_find_node_by_path("/");
  359. if (!root)
  360. return -EINVAL;
  361. for_each_available_child_of_node(root, child) {
  362. if (of_device_is_compatible(child, "arm,pl111")) {
  363. has_coretile_clcd = true;
  364. ct_clcd = child;
  365. of_node_put(child);
  366. break;
  367. }
  368. if (of_device_is_compatible(child, "arm,hdlcd")) {
  369. has_coretile_hdlcd = true;
  370. of_node_put(child);
  371. break;
  372. }
  373. }
  374. of_node_put(root);
  375. /*
  376. * If there is a coretile HDLCD and it has a driver,
  377. * do not mux the CLCD on the motherboard to the DVI.
  378. */
  379. if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD))
  380. mux_motherboard = false;
  381. /*
  382. * On the Vexpress CA9 we let the CLCD on the coretile
  383. * take precedence, so also in this case do not mux the
  384. * motherboard to the DVI.
  385. */
  386. if (has_coretile_clcd)
  387. mux_motherboard = false;
  388. if (mux_motherboard) {
  389. dev_info(dev, "DVI muxed to motherboard CLCD\n");
  390. val = VEXPRESS_FPGAMUX_MOTHERBOARD;
  391. } else if (ct_clcd == dev->of_node) {
  392. dev_info(dev,
  393. "DVI muxed to daughterboard 1 (core tile) CLCD\n");
  394. val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1;
  395. } else {
  396. dev_info(dev, "core tile graphics present\n");
  397. dev_info(dev, "this device will be deactivated\n");
  398. return -ENODEV;
  399. }
  400. /* Call into deep Vexpress configuration API */
  401. pdev = of_find_device_by_node(np);
  402. if (!pdev) {
  403. dev_err(dev, "can't find the sysreg device, deferring\n");
  404. return -EPROBE_DEFER;
  405. }
  406. map = devm_regmap_init_vexpress_config(&pdev->dev);
  407. if (IS_ERR(map)) {
  408. platform_device_put(pdev);
  409. return PTR_ERR(map);
  410. }
  411. ret = regmap_write(map, 0, val);
  412. platform_device_put(pdev);
  413. if (ret) {
  414. dev_err(dev, "error setting DVI muxmode\n");
  415. return -ENODEV;
  416. }
  417. priv->variant = &pl111_vexpress;
  418. dev_info(dev, "initializing Versatile Express PL111\n");
  419. return 0;
  420. }
  421. int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
  422. {
  423. const struct of_device_id *clcd_id;
  424. enum versatile_clcd versatile_clcd_type;
  425. struct device_node *np;
  426. struct regmap *map;
  427. np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
  428. &clcd_id);
  429. if (!np) {
  430. /* Non-ARM reference designs, just bail out */
  431. return 0;
  432. }
  433. versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
  434. /* Versatile Express special handling */
  435. if (versatile_clcd_type == VEXPRESS_CLCD_V2M) {
  436. int ret = pl111_vexpress_clcd_init(dev, np, priv);
  437. of_node_put(np);
  438. if (ret)
  439. dev_err(dev, "Versatile Express init failed - %d", ret);
  440. return ret;
  441. }
  442. /*
  443. * On the Integrator, check if we should use the IM-PD1 instead,
  444. * if we find it, it will take precedence. This is on the Integrator/AP
  445. * which only has this option for PL110 graphics.
  446. */
  447. if (versatile_clcd_type == INTEGRATOR_CLCD_CM) {
  448. np = of_find_matching_node_and_match(NULL, impd1_clcd_of_match,
  449. &clcd_id);
  450. if (np)
  451. versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
  452. }
  453. map = syscon_node_to_regmap(np);
  454. of_node_put(np);
  455. if (IS_ERR(map)) {
  456. dev_err(dev, "no Versatile syscon regmap\n");
  457. return PTR_ERR(map);
  458. }
  459. switch (versatile_clcd_type) {
  460. case INTEGRATOR_CLCD_CM:
  461. versatile_syscon_map = map;
  462. priv->variant = &pl110_integrator;
  463. priv->variant_display_enable = pl111_integrator_enable;
  464. dev_info(dev, "set up callbacks for Integrator PL110\n");
  465. break;
  466. case INTEGRATOR_IMPD1:
  467. versatile_syscon_map = map;
  468. priv->variant = &pl110_impd1;
  469. priv->variant_display_enable = pl111_impd1_enable;
  470. priv->variant_display_disable = pl111_impd1_disable;
  471. dev_info(dev, "set up callbacks for IM-PD1 PL110\n");
  472. break;
  473. case VERSATILE_CLCD:
  474. versatile_syscon_map = map;
  475. /* This can do RGB565 with external PLD */
  476. priv->variant = &pl110_versatile;
  477. priv->variant_display_enable = pl111_versatile_enable;
  478. priv->variant_display_disable = pl111_versatile_disable;
  479. /*
  480. * The Versatile has a variant halfway between PL110
  481. * and PL111 where these two registers have already been
  482. * swapped.
  483. */
  484. priv->ienb = CLCD_PL111_IENB;
  485. priv->ctrl = CLCD_PL111_CNTL;
  486. dev_info(dev, "set up callbacks for Versatile PL110\n");
  487. break;
  488. case REALVIEW_CLCD_EB:
  489. case REALVIEW_CLCD_PB1176:
  490. case REALVIEW_CLCD_PB11MP:
  491. case REALVIEW_CLCD_PBA8:
  492. case REALVIEW_CLCD_PBX:
  493. versatile_syscon_map = map;
  494. priv->variant = &pl111_realview;
  495. priv->variant_display_enable = pl111_realview_clcd_enable;
  496. priv->variant_display_disable = pl111_realview_clcd_disable;
  497. dev_info(dev, "set up callbacks for RealView PL111\n");
  498. break;
  499. default:
  500. dev_info(dev, "unknown Versatile system controller\n");
  501. break;
  502. }
  503. return 0;
  504. }
  505. EXPORT_SYMBOL_GPL(pl111_versatile_init);