brintos

brintos / linux-shallow public Read only

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