meson_drv.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2016 BayLibre, SAS
  4. * Author: Neil Armstrong <narmstrong@baylibre.com>
  5. * Copyright (C) 2014 Endless Mobile
  6. *
  7. * Written by:
  8. * Jasper St. Pierre <jstpierre@mecheye.net>
  9. */
  10. #include <linux/aperture.h>
  11. #include <linux/component.h>
  12. #include <linux/module.h>
  13. #include <linux/of_graph.h>
  14. #include <linux/sys_soc.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/soc/amlogic/meson-canvas.h>
  17. #include <drm/clients/drm_client_setup.h>
  18. #include <drm/drm_atomic_helper.h>
  19. #include <drm/drm_drv.h>
  20. #include <drm/drm_fbdev_dma.h>
  21. #include <drm/drm_gem_dma_helper.h>
  22. #include <drm/drm_gem_framebuffer_helper.h>
  23. #include <drm/drm_modeset_helper_vtables.h>
  24. #include <drm/drm_module.h>
  25. #include <drm/drm_probe_helper.h>
  26. #include <drm/drm_vblank.h>
  27. #include "meson_crtc.h"
  28. #include "meson_drv.h"
  29. #include "meson_overlay.h"
  30. #include "meson_plane.h"
  31. #include "meson_osd_afbcd.h"
  32. #include "meson_registers.h"
  33. #include "meson_encoder_cvbs.h"
  34. #include "meson_encoder_hdmi.h"
  35. #include "meson_encoder_dsi.h"
  36. #include "meson_viu.h"
  37. #include "meson_vpp.h"
  38. #include "meson_rdma.h"
  39. #define DRIVER_NAME "meson"
  40. #define DRIVER_DESC "Amlogic Meson DRM driver"
  41. /**
  42. * DOC: Video Processing Unit
  43. *
  44. * VPU Handles the Global Video Processing, it includes management of the
  45. * clocks gates, blocks reset lines and power domains.
  46. *
  47. * What is missing :
  48. *
  49. * - Full reset of entire video processing HW blocks
  50. * - Scaling and setup of the VPU clock
  51. * - Bus clock gates
  52. * - Powering up video processing HW blocks
  53. * - Powering Up HDMI controller and PHY
  54. */
  55. static const struct drm_mode_config_funcs meson_mode_config_funcs = {
  56. .atomic_check = drm_atomic_helper_check,
  57. .atomic_commit = drm_atomic_helper_commit,
  58. .fb_create = drm_gem_fb_create,
  59. };
  60. static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = {
  61. .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
  62. };
  63. static irqreturn_t meson_irq(int irq, void *arg)
  64. {
  65. struct drm_device *dev = arg;
  66. struct meson_drm *priv = dev->dev_private;
  67. (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
  68. meson_crtc_irq(priv);
  69. return IRQ_HANDLED;
  70. }
  71. static int meson_dumb_create(struct drm_file *file, struct drm_device *dev,
  72. struct drm_mode_create_dumb *args)
  73. {
  74. /*
  75. * We need 64bytes aligned stride, and PAGE aligned size
  76. */
  77. args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64);
  78. args->size = PAGE_ALIGN(args->pitch * args->height);
  79. return drm_gem_dma_dumb_create_internal(file, dev, args);
  80. }
  81. DEFINE_DRM_GEM_DMA_FOPS(fops);
  82. static const struct drm_driver meson_driver = {
  83. .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
  84. /* DMA Ops */
  85. DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
  86. DRM_FBDEV_DMA_DRIVER_OPS,
  87. /* Misc */
  88. .fops = &fops,
  89. .name = DRIVER_NAME,
  90. .desc = DRIVER_DESC,
  91. .major = 1,
  92. .minor = 0,
  93. };
  94. static bool meson_vpu_has_available_connectors(struct device *dev)
  95. {
  96. struct device_node *ep, *remote;
  97. /* Parses each endpoint and check if remote exists */
  98. for_each_endpoint_of_node(dev->of_node, ep) {
  99. /* If the endpoint node exists, consider it enabled */
  100. remote = of_graph_get_remote_port(ep);
  101. if (remote) {
  102. of_node_put(remote);
  103. of_node_put(ep);
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. static const struct regmap_config meson_regmap_config = {
  110. .reg_bits = 32,
  111. .val_bits = 32,
  112. .reg_stride = 4,
  113. .max_register = 0x1000,
  114. };
  115. static void meson_vpu_init(struct meson_drm *priv)
  116. {
  117. u32 value;
  118. /*
  119. * Slave dc0 and dc5 connected to master port 1.
  120. * By default other slaves are connected to master port 0.
  121. */
  122. value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
  123. VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
  124. writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
  125. /* Slave dc0 connected to master port 1 */
  126. value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
  127. writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
  128. /* Slave dc4 and dc7 connected to master port 1 */
  129. value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
  130. VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
  131. writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
  132. /* Slave dc1 connected to master port 1 */
  133. value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
  134. writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
  135. }
  136. struct meson_drm_soc_attr {
  137. struct meson_drm_soc_limits limits;
  138. const struct soc_device_attribute *attrs;
  139. };
  140. static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
  141. /* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
  142. {
  143. .limits = {
  144. .max_hdmi_phy_freq = 1650000000,
  145. },
  146. .attrs = (const struct soc_device_attribute []) {
  147. { .soc_id = "GXL (S805*)", },
  148. { /* sentinel */ }
  149. }
  150. },
  151. };
  152. static int meson_drv_bind_master(struct device *dev, bool has_components)
  153. {
  154. struct platform_device *pdev = to_platform_device(dev);
  155. const struct meson_drm_match_data *match;
  156. struct meson_drm *priv;
  157. struct drm_device *drm;
  158. struct resource *res;
  159. void __iomem *regs;
  160. int ret, i;
  161. /* Checks if an output connector is available */
  162. if (!meson_vpu_has_available_connectors(dev)) {
  163. dev_err(dev, "No output connector available\n");
  164. return -ENODEV;
  165. }
  166. match = of_device_get_match_data(dev);
  167. if (!match)
  168. return -ENODEV;
  169. drm = drm_dev_alloc(&meson_driver, dev);
  170. if (IS_ERR(drm))
  171. return PTR_ERR(drm);
  172. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  173. if (!priv) {
  174. ret = -ENOMEM;
  175. goto free_drm;
  176. }
  177. drm->dev_private = priv;
  178. priv->drm = drm;
  179. priv->dev = dev;
  180. priv->compat = match->compat;
  181. priv->afbcd.ops = match->afbcd_ops;
  182. regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
  183. if (IS_ERR(regs)) {
  184. ret = PTR_ERR(regs);
  185. goto free_drm;
  186. }
  187. priv->io_base = regs;
  188. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
  189. if (!res) {
  190. ret = -EINVAL;
  191. goto free_drm;
  192. }
  193. /* Simply ioremap since it may be a shared register zone */
  194. regs = devm_ioremap(dev, res->start, resource_size(res));
  195. if (!regs) {
  196. ret = -EADDRNOTAVAIL;
  197. goto free_drm;
  198. }
  199. priv->hhi = devm_regmap_init_mmio(dev, regs,
  200. &meson_regmap_config);
  201. if (IS_ERR(priv->hhi)) {
  202. dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
  203. ret = PTR_ERR(priv->hhi);
  204. goto free_drm;
  205. }
  206. priv->canvas = meson_canvas_get(dev);
  207. if (IS_ERR(priv->canvas)) {
  208. ret = PTR_ERR(priv->canvas);
  209. goto free_drm;
  210. }
  211. ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
  212. if (ret)
  213. goto free_drm;
  214. ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
  215. if (ret)
  216. goto free_canvas_osd1;
  217. ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
  218. if (ret)
  219. goto free_canvas_vd1_0;
  220. ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
  221. if (ret)
  222. goto free_canvas_vd1_1;
  223. priv->vsync_irq = platform_get_irq(pdev, 0);
  224. ret = drm_vblank_init(drm, 1);
  225. if (ret)
  226. goto free_canvas_vd1_2;
  227. /* Assign limits per soc revision/package */
  228. for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) {
  229. if (soc_device_match(meson_drm_soc_attrs[i].attrs)) {
  230. priv->limits = &meson_drm_soc_attrs[i].limits;
  231. break;
  232. }
  233. }
  234. /*
  235. * Remove early framebuffers (ie. simplefb). The framebuffer can be
  236. * located anywhere in RAM
  237. */
  238. ret = aperture_remove_all_conflicting_devices(meson_driver.name);
  239. if (ret)
  240. goto free_canvas_vd1_2;
  241. ret = drmm_mode_config_init(drm);
  242. if (ret)
  243. goto free_canvas_vd1_2;
  244. drm->mode_config.max_width = 3840;
  245. drm->mode_config.max_height = 2160;
  246. drm->mode_config.funcs = &meson_mode_config_funcs;
  247. drm->mode_config.helper_private = &meson_mode_config_helpers;
  248. /* Hardware Initialization */
  249. meson_vpu_init(priv);
  250. meson_venc_init(priv);
  251. meson_vpp_init(priv);
  252. meson_viu_init(priv);
  253. if (priv->afbcd.ops) {
  254. ret = priv->afbcd.ops->init(priv);
  255. if (ret)
  256. goto free_canvas_vd1_2;
  257. }
  258. /* Encoder Initialization */
  259. ret = meson_encoder_cvbs_probe(priv);
  260. if (ret)
  261. goto exit_afbcd;
  262. if (has_components) {
  263. ret = component_bind_all(dev, drm);
  264. if (ret) {
  265. dev_err(drm->dev, "Couldn't bind all components\n");
  266. /* Do not try to unbind */
  267. has_components = false;
  268. goto exit_afbcd;
  269. }
  270. }
  271. ret = meson_encoder_hdmi_probe(priv);
  272. if (ret)
  273. goto exit_afbcd;
  274. if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
  275. ret = meson_encoder_dsi_probe(priv);
  276. if (ret)
  277. goto exit_afbcd;
  278. }
  279. ret = meson_plane_create(priv);
  280. if (ret)
  281. goto exit_afbcd;
  282. ret = meson_overlay_create(priv);
  283. if (ret)
  284. goto exit_afbcd;
  285. ret = meson_crtc_create(priv);
  286. if (ret)
  287. goto exit_afbcd;
  288. ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm);
  289. if (ret)
  290. goto exit_afbcd;
  291. drm_mode_config_reset(drm);
  292. drm_kms_helper_poll_init(drm);
  293. platform_set_drvdata(pdev, priv);
  294. ret = drm_dev_register(drm, 0);
  295. if (ret)
  296. goto uninstall_irq;
  297. drm_client_setup(drm, NULL);
  298. return 0;
  299. uninstall_irq:
  300. free_irq(priv->vsync_irq, drm);
  301. exit_afbcd:
  302. if (priv->afbcd.ops)
  303. priv->afbcd.ops->exit(priv);
  304. free_canvas_vd1_2:
  305. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
  306. free_canvas_vd1_1:
  307. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
  308. free_canvas_vd1_0:
  309. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
  310. free_canvas_osd1:
  311. meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
  312. free_drm:
  313. drm_dev_put(drm);
  314. meson_encoder_dsi_remove(priv);
  315. meson_encoder_hdmi_remove(priv);
  316. meson_encoder_cvbs_remove(priv);
  317. if (has_components)
  318. component_unbind_all(dev, drm);
  319. return ret;
  320. }
  321. static int meson_drv_bind(struct device *dev)
  322. {
  323. return meson_drv_bind_master(dev, true);
  324. }
  325. static void meson_drv_unbind(struct device *dev)
  326. {
  327. struct meson_drm *priv = dev_get_drvdata(dev);
  328. struct drm_device *drm = priv->drm;
  329. if (priv->canvas) {
  330. meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
  331. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
  332. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
  333. meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
  334. }
  335. drm_dev_unregister(drm);
  336. drm_kms_helper_poll_fini(drm);
  337. drm_atomic_helper_shutdown(drm);
  338. free_irq(priv->vsync_irq, drm);
  339. drm_dev_put(drm);
  340. meson_encoder_dsi_remove(priv);
  341. meson_encoder_hdmi_remove(priv);
  342. meson_encoder_cvbs_remove(priv);
  343. component_unbind_all(dev, drm);
  344. if (priv->afbcd.ops)
  345. priv->afbcd.ops->exit(priv);
  346. }
  347. static const struct component_master_ops meson_drv_master_ops = {
  348. .bind = meson_drv_bind,
  349. .unbind = meson_drv_unbind,
  350. };
  351. static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
  352. {
  353. struct meson_drm *priv = dev_get_drvdata(dev);
  354. if (!priv)
  355. return 0;
  356. return drm_mode_config_helper_suspend(priv->drm);
  357. }
  358. static int __maybe_unused meson_drv_pm_resume(struct device *dev)
  359. {
  360. struct meson_drm *priv = dev_get_drvdata(dev);
  361. if (!priv)
  362. return 0;
  363. meson_vpu_init(priv);
  364. meson_venc_init(priv);
  365. meson_vpp_init(priv);
  366. meson_viu_init(priv);
  367. if (priv->afbcd.ops)
  368. priv->afbcd.ops->init(priv);
  369. return drm_mode_config_helper_resume(priv->drm);
  370. }
  371. static void meson_drv_shutdown(struct platform_device *pdev)
  372. {
  373. struct meson_drm *priv = dev_get_drvdata(&pdev->dev);
  374. if (!priv)
  375. return;
  376. drm_kms_helper_poll_fini(priv->drm);
  377. drm_atomic_helper_shutdown(priv->drm);
  378. }
  379. /*
  380. * Only devices to use as components
  381. * TOFIX: get rid of components when we can finally
  382. * get meson_dx_hdmi to stop using the meson_drm
  383. * private structure for HHI registers.
  384. */
  385. static const struct of_device_id components_dev_match[] = {
  386. { .compatible = "amlogic,meson-gxbb-dw-hdmi" },
  387. { .compatible = "amlogic,meson-gxl-dw-hdmi" },
  388. { .compatible = "amlogic,meson-gxm-dw-hdmi" },
  389. { .compatible = "amlogic,meson-g12a-dw-hdmi" },
  390. {}
  391. };
  392. static int meson_drv_probe(struct platform_device *pdev)
  393. {
  394. struct component_match *match = NULL;
  395. struct device_node *np = pdev->dev.of_node;
  396. struct device_node *ep, *remote;
  397. int count = 0;
  398. for_each_endpoint_of_node(np, ep) {
  399. remote = of_graph_get_remote_port_parent(ep);
  400. if (!remote || !of_device_is_available(remote)) {
  401. of_node_put(remote);
  402. continue;
  403. }
  404. if (of_match_node(components_dev_match, remote)) {
  405. component_match_add(&pdev->dev, &match, component_compare_of, remote);
  406. dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n",
  407. np, remote, dev_name(&pdev->dev));
  408. }
  409. of_node_put(remote);
  410. ++count;
  411. }
  412. if (count && !match)
  413. return meson_drv_bind_master(&pdev->dev, false);
  414. /* If some endpoints were found, initialize the nodes */
  415. if (count) {
  416. dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count);
  417. return component_master_add_with_match(&pdev->dev,
  418. &meson_drv_master_ops,
  419. match);
  420. }
  421. /* If no output endpoints were available, simply bail out */
  422. return 0;
  423. };
  424. static void meson_drv_remove(struct platform_device *pdev)
  425. {
  426. component_master_del(&pdev->dev, &meson_drv_master_ops);
  427. }
  428. static struct meson_drm_match_data meson_drm_gxbb_data = {
  429. .compat = VPU_COMPATIBLE_GXBB,
  430. };
  431. static struct meson_drm_match_data meson_drm_gxl_data = {
  432. .compat = VPU_COMPATIBLE_GXL,
  433. };
  434. static struct meson_drm_match_data meson_drm_gxm_data = {
  435. .compat = VPU_COMPATIBLE_GXM,
  436. .afbcd_ops = &meson_afbcd_gxm_ops,
  437. };
  438. static struct meson_drm_match_data meson_drm_g12a_data = {
  439. .compat = VPU_COMPATIBLE_G12A,
  440. .afbcd_ops = &meson_afbcd_g12a_ops,
  441. };
  442. static const struct of_device_id dt_match[] = {
  443. { .compatible = "amlogic,meson-gxbb-vpu",
  444. .data = (void *)&meson_drm_gxbb_data },
  445. { .compatible = "amlogic,meson-gxl-vpu",
  446. .data = (void *)&meson_drm_gxl_data },
  447. { .compatible = "amlogic,meson-gxm-vpu",
  448. .data = (void *)&meson_drm_gxm_data },
  449. { .compatible = "amlogic,meson-g12a-vpu",
  450. .data = (void *)&meson_drm_g12a_data },
  451. {}
  452. };
  453. MODULE_DEVICE_TABLE(of, dt_match);
  454. static const struct dev_pm_ops meson_drv_pm_ops = {
  455. SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume)
  456. };
  457. static struct platform_driver meson_drm_platform_driver = {
  458. .probe = meson_drv_probe,
  459. .remove = meson_drv_remove,
  460. .shutdown = meson_drv_shutdown,
  461. .driver = {
  462. .name = "meson-drm",
  463. .of_match_table = dt_match,
  464. .pm = &meson_drv_pm_ops,
  465. },
  466. };
  467. drm_module_platform_driver(meson_drm_platform_driver);
  468. MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
  469. MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
  470. MODULE_DESCRIPTION(DRIVER_DESC);
  471. MODULE_LICENSE("GPL");