xref: /openbmc/linux/drivers/gpu/drm/tiny/arcpgu.c (revision 4144334a)
13ade7a69SDaniel Vetter // SPDX-License-Identifier: GPL-2.0-only
23ade7a69SDaniel Vetter /*
33ade7a69SDaniel Vetter  * ARC PGU DRM driver.
43ade7a69SDaniel Vetter  *
53ade7a69SDaniel Vetter  * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
63ade7a69SDaniel Vetter  */
73ade7a69SDaniel Vetter 
83ade7a69SDaniel Vetter #include <linux/clk.h>
93ade7a69SDaniel Vetter #include <drm/drm_atomic_helper.h>
103ade7a69SDaniel Vetter #include <drm/drm_debugfs.h>
113ade7a69SDaniel Vetter #include <drm/drm_device.h>
123ade7a69SDaniel Vetter #include <drm/drm_drv.h>
13255490f9SVille Syrjälä #include <drm/drm_edid.h>
146bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
15*4144334aSThomas Zimmermann #include <drm/drm_fbdev_dma.h>
163ade7a69SDaniel Vetter #include <drm/drm_fourcc.h>
17720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
184a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
193ade7a69SDaniel Vetter #include <drm/drm_gem_framebuffer_helper.h>
208acd15a0SJavier Martinez Canillas #include <drm/drm_module.h>
213ade7a69SDaniel Vetter #include <drm/drm_of.h>
223ade7a69SDaniel Vetter #include <drm/drm_probe_helper.h>
233ade7a69SDaniel Vetter #include <drm/drm_simple_kms_helper.h>
243ade7a69SDaniel Vetter #include <linux/dma-mapping.h>
253ade7a69SDaniel Vetter #include <linux/module.h>
263ade7a69SDaniel Vetter #include <linux/of_reserved_mem.h>
273ade7a69SDaniel Vetter #include <linux/platform_device.h>
283ade7a69SDaniel Vetter 
293ade7a69SDaniel Vetter #define ARCPGU_REG_CTRL		0x00
303ade7a69SDaniel Vetter #define ARCPGU_REG_STAT		0x04
313ade7a69SDaniel Vetter #define ARCPGU_REG_FMT		0x10
323ade7a69SDaniel Vetter #define ARCPGU_REG_HSYNC	0x14
333ade7a69SDaniel Vetter #define ARCPGU_REG_VSYNC	0x18
343ade7a69SDaniel Vetter #define ARCPGU_REG_ACTIVE	0x1c
353ade7a69SDaniel Vetter #define ARCPGU_REG_BUF0_ADDR	0x40
363ade7a69SDaniel Vetter #define ARCPGU_REG_STRIDE	0x50
373ade7a69SDaniel Vetter #define ARCPGU_REG_START_SET	0x84
383ade7a69SDaniel Vetter 
393ade7a69SDaniel Vetter #define ARCPGU_REG_ID		0x3FC
403ade7a69SDaniel Vetter 
413ade7a69SDaniel Vetter #define ARCPGU_CTRL_ENABLE_MASK	0x02
423ade7a69SDaniel Vetter #define ARCPGU_CTRL_VS_POL_MASK	0x1
433ade7a69SDaniel Vetter #define ARCPGU_CTRL_VS_POL_OFST	0x3
443ade7a69SDaniel Vetter #define ARCPGU_CTRL_HS_POL_MASK	0x1
453ade7a69SDaniel Vetter #define ARCPGU_CTRL_HS_POL_OFST	0x4
463ade7a69SDaniel Vetter #define ARCPGU_MODE_XRGB8888	BIT(2)
473ade7a69SDaniel Vetter #define ARCPGU_STAT_BUSY_MASK	0x02
483ade7a69SDaniel Vetter 
493ade7a69SDaniel Vetter struct arcpgu_drm_private {
503ade7a69SDaniel Vetter 	struct drm_device	drm;
513ade7a69SDaniel Vetter 	void __iomem		*regs;
523ade7a69SDaniel Vetter 	struct clk		*clk;
533ade7a69SDaniel Vetter 	struct drm_simple_display_pipe pipe;
543ade7a69SDaniel Vetter 	struct drm_connector	sim_conn;
553ade7a69SDaniel Vetter };
563ade7a69SDaniel Vetter 
573ade7a69SDaniel Vetter #define dev_to_arcpgu(x) container_of(x, struct arcpgu_drm_private, drm)
583ade7a69SDaniel Vetter 
593ade7a69SDaniel Vetter #define pipe_to_arcpgu_priv(x) container_of(x, struct arcpgu_drm_private, pipe)
603ade7a69SDaniel Vetter 
arc_pgu_write(struct arcpgu_drm_private * arcpgu,unsigned int reg,u32 value)613ade7a69SDaniel Vetter static inline void arc_pgu_write(struct arcpgu_drm_private *arcpgu,
623ade7a69SDaniel Vetter 				 unsigned int reg, u32 value)
633ade7a69SDaniel Vetter {
643ade7a69SDaniel Vetter 	iowrite32(value, arcpgu->regs + reg);
653ade7a69SDaniel Vetter }
663ade7a69SDaniel Vetter 
arc_pgu_read(struct arcpgu_drm_private * arcpgu,unsigned int reg)673ade7a69SDaniel Vetter static inline u32 arc_pgu_read(struct arcpgu_drm_private *arcpgu,
683ade7a69SDaniel Vetter 			       unsigned int reg)
693ade7a69SDaniel Vetter {
703ade7a69SDaniel Vetter 	return ioread32(arcpgu->regs + reg);
713ade7a69SDaniel Vetter }
723ade7a69SDaniel Vetter 
733ade7a69SDaniel Vetter #define XRES_DEF	640
743ade7a69SDaniel Vetter #define YRES_DEF	480
753ade7a69SDaniel Vetter 
763ade7a69SDaniel Vetter #define XRES_MAX	8192
773ade7a69SDaniel Vetter #define YRES_MAX	8192
783ade7a69SDaniel Vetter 
arcpgu_drm_connector_get_modes(struct drm_connector * connector)793ade7a69SDaniel Vetter static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
803ade7a69SDaniel Vetter {
813ade7a69SDaniel Vetter 	int count;
823ade7a69SDaniel Vetter 
833ade7a69SDaniel Vetter 	count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX);
843ade7a69SDaniel Vetter 	drm_set_preferred_mode(connector, XRES_DEF, YRES_DEF);
853ade7a69SDaniel Vetter 	return count;
863ade7a69SDaniel Vetter }
873ade7a69SDaniel Vetter 
883ade7a69SDaniel Vetter static const struct drm_connector_helper_funcs
893ade7a69SDaniel Vetter arcpgu_drm_connector_helper_funcs = {
903ade7a69SDaniel Vetter 	.get_modes = arcpgu_drm_connector_get_modes,
913ade7a69SDaniel Vetter };
923ade7a69SDaniel Vetter 
933ade7a69SDaniel Vetter static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
943ade7a69SDaniel Vetter 	.reset = drm_atomic_helper_connector_reset,
953ade7a69SDaniel Vetter 	.fill_modes = drm_helper_probe_single_connector_modes,
963ade7a69SDaniel Vetter 	.destroy = drm_connector_cleanup,
973ade7a69SDaniel Vetter 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
983ade7a69SDaniel Vetter 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
993ade7a69SDaniel Vetter };
1003ade7a69SDaniel Vetter 
arcpgu_drm_sim_init(struct drm_device * drm,struct drm_connector * connector)1013ade7a69SDaniel Vetter static int arcpgu_drm_sim_init(struct drm_device *drm, struct drm_connector *connector)
1023ade7a69SDaniel Vetter {
1033ade7a69SDaniel Vetter 	drm_connector_helper_add(connector, &arcpgu_drm_connector_helper_funcs);
1043ade7a69SDaniel Vetter 	return drm_connector_init(drm, connector, &arcpgu_drm_connector_funcs,
1053ade7a69SDaniel Vetter 				  DRM_MODE_CONNECTOR_VIRTUAL);
1063ade7a69SDaniel Vetter }
1073ade7a69SDaniel Vetter 
1083ade7a69SDaniel Vetter #define ENCODE_PGU_XY(x, y)	((((x) - 1) << 16) | ((y) - 1))
1093ade7a69SDaniel Vetter 
1103ade7a69SDaniel Vetter static const u32 arc_pgu_supported_formats[] = {
1113ade7a69SDaniel Vetter 	DRM_FORMAT_RGB565,
1123ade7a69SDaniel Vetter 	DRM_FORMAT_XRGB8888,
1133ade7a69SDaniel Vetter 	DRM_FORMAT_ARGB8888,
1143ade7a69SDaniel Vetter };
1153ade7a69SDaniel Vetter 
arc_pgu_set_pxl_fmt(struct arcpgu_drm_private * arcpgu)1163ade7a69SDaniel Vetter static void arc_pgu_set_pxl_fmt(struct arcpgu_drm_private *arcpgu)
1173ade7a69SDaniel Vetter {
1183ade7a69SDaniel Vetter 	const struct drm_framebuffer *fb = arcpgu->pipe.plane.state->fb;
1193ade7a69SDaniel Vetter 	uint32_t pixel_format = fb->format->format;
1203ade7a69SDaniel Vetter 	u32 format = DRM_FORMAT_INVALID;
1213ade7a69SDaniel Vetter 	int i;
1223ade7a69SDaniel Vetter 	u32 reg_ctrl;
1233ade7a69SDaniel Vetter 
1243ade7a69SDaniel Vetter 	for (i = 0; i < ARRAY_SIZE(arc_pgu_supported_formats); i++) {
1253ade7a69SDaniel Vetter 		if (arc_pgu_supported_formats[i] == pixel_format)
1263ade7a69SDaniel Vetter 			format = arc_pgu_supported_formats[i];
1273ade7a69SDaniel Vetter 	}
1283ade7a69SDaniel Vetter 
1293ade7a69SDaniel Vetter 	if (WARN_ON(format == DRM_FORMAT_INVALID))
1303ade7a69SDaniel Vetter 		return;
1313ade7a69SDaniel Vetter 
1323ade7a69SDaniel Vetter 	reg_ctrl = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
1333ade7a69SDaniel Vetter 	if (format == DRM_FORMAT_RGB565)
1343ade7a69SDaniel Vetter 		reg_ctrl &= ~ARCPGU_MODE_XRGB8888;
1353ade7a69SDaniel Vetter 	else
1363ade7a69SDaniel Vetter 		reg_ctrl |= ARCPGU_MODE_XRGB8888;
1373ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl);
1383ade7a69SDaniel Vetter }
1393ade7a69SDaniel Vetter 
arc_pgu_mode_valid(struct drm_simple_display_pipe * pipe,const struct drm_display_mode * mode)1403ade7a69SDaniel Vetter static enum drm_mode_status arc_pgu_mode_valid(struct drm_simple_display_pipe *pipe,
1413ade7a69SDaniel Vetter 					       const struct drm_display_mode *mode)
1423ade7a69SDaniel Vetter {
1433ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
1443ade7a69SDaniel Vetter 	long rate, clk_rate = mode->clock * 1000;
1453ade7a69SDaniel Vetter 	long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */
1463ade7a69SDaniel Vetter 
1473ade7a69SDaniel Vetter 	rate = clk_round_rate(arcpgu->clk, clk_rate);
1483ade7a69SDaniel Vetter 	if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0))
1493ade7a69SDaniel Vetter 		return MODE_OK;
1503ade7a69SDaniel Vetter 
1513ade7a69SDaniel Vetter 	return MODE_NOCLOCK;
1523ade7a69SDaniel Vetter }
1533ade7a69SDaniel Vetter 
arc_pgu_mode_set(struct arcpgu_drm_private * arcpgu)1543ade7a69SDaniel Vetter static void arc_pgu_mode_set(struct arcpgu_drm_private *arcpgu)
1553ade7a69SDaniel Vetter {
1563ade7a69SDaniel Vetter 	struct drm_display_mode *m = &arcpgu->pipe.crtc.state->adjusted_mode;
1573ade7a69SDaniel Vetter 	u32 val;
1583ade7a69SDaniel Vetter 
1593ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
1603ade7a69SDaniel Vetter 		      ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal));
1613ade7a69SDaniel Vetter 
1623ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC,
1633ade7a69SDaniel Vetter 		      ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay,
1643ade7a69SDaniel Vetter 				    m->crtc_hsync_end - m->crtc_hdisplay));
1653ade7a69SDaniel Vetter 
1663ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC,
1673ade7a69SDaniel Vetter 		      ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay,
1683ade7a69SDaniel Vetter 				    m->crtc_vsync_end - m->crtc_vdisplay));
1693ade7a69SDaniel Vetter 
1703ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE,
1713ade7a69SDaniel Vetter 		      ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start,
1723ade7a69SDaniel Vetter 				    m->crtc_vblank_end - m->crtc_vblank_start));
1733ade7a69SDaniel Vetter 
1743ade7a69SDaniel Vetter 	val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
1753ade7a69SDaniel Vetter 
1763ade7a69SDaniel Vetter 	if (m->flags & DRM_MODE_FLAG_PVSYNC)
1773ade7a69SDaniel Vetter 		val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST;
1783ade7a69SDaniel Vetter 	else
1793ade7a69SDaniel Vetter 		val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST);
1803ade7a69SDaniel Vetter 
1813ade7a69SDaniel Vetter 	if (m->flags & DRM_MODE_FLAG_PHSYNC)
1823ade7a69SDaniel Vetter 		val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST;
1833ade7a69SDaniel Vetter 	else
1843ade7a69SDaniel Vetter 		val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST);
1853ade7a69SDaniel Vetter 
1863ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val);
1873ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0);
1883ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1);
1893ade7a69SDaniel Vetter 
1903ade7a69SDaniel Vetter 	arc_pgu_set_pxl_fmt(arcpgu);
1913ade7a69SDaniel Vetter 
1923ade7a69SDaniel Vetter 	clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
1933ade7a69SDaniel Vetter }
1943ade7a69SDaniel Vetter 
arc_pgu_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)1953ade7a69SDaniel Vetter static void arc_pgu_enable(struct drm_simple_display_pipe *pipe,
1963ade7a69SDaniel Vetter 			   struct drm_crtc_state *crtc_state,
1973ade7a69SDaniel Vetter 			   struct drm_plane_state *plane_state)
1983ade7a69SDaniel Vetter {
1993ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
2003ade7a69SDaniel Vetter 
2013ade7a69SDaniel Vetter 	arc_pgu_mode_set(arcpgu);
2023ade7a69SDaniel Vetter 
2033ade7a69SDaniel Vetter 	clk_prepare_enable(arcpgu->clk);
2043ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
2053ade7a69SDaniel Vetter 		      arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
2063ade7a69SDaniel Vetter 		      ARCPGU_CTRL_ENABLE_MASK);
2073ade7a69SDaniel Vetter }
2083ade7a69SDaniel Vetter 
arc_pgu_disable(struct drm_simple_display_pipe * pipe)2093ade7a69SDaniel Vetter static void arc_pgu_disable(struct drm_simple_display_pipe *pipe)
2103ade7a69SDaniel Vetter {
2113ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu = pipe_to_arcpgu_priv(pipe);
2123ade7a69SDaniel Vetter 
2133ade7a69SDaniel Vetter 	clk_disable_unprepare(arcpgu->clk);
2143ade7a69SDaniel Vetter 	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
2153ade7a69SDaniel Vetter 			      arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) &
2163ade7a69SDaniel Vetter 			      ~ARCPGU_CTRL_ENABLE_MASK);
2173ade7a69SDaniel Vetter }
2183ade7a69SDaniel Vetter 
arc_pgu_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * state)2193ade7a69SDaniel Vetter static void arc_pgu_update(struct drm_simple_display_pipe *pipe,
2203ade7a69SDaniel Vetter 			   struct drm_plane_state *state)
2213ade7a69SDaniel Vetter {
2223ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu;
2234a83c26aSDanilo Krummrich 	struct drm_gem_dma_object *gem;
2243ade7a69SDaniel Vetter 
2253ade7a69SDaniel Vetter 	if (!pipe->plane.state->fb)
2263ade7a69SDaniel Vetter 		return;
2273ade7a69SDaniel Vetter 
2283ade7a69SDaniel Vetter 	arcpgu = pipe_to_arcpgu_priv(pipe);
2296bcfe8eaSDanilo Krummrich 	gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
2308c30eeccSDanilo Krummrich 	arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->dma_addr);
2313ade7a69SDaniel Vetter }
2323ade7a69SDaniel Vetter 
2333ade7a69SDaniel Vetter static const struct drm_simple_display_pipe_funcs arc_pgu_pipe_funcs = {
2343ade7a69SDaniel Vetter 	.update = arc_pgu_update,
2353ade7a69SDaniel Vetter 	.mode_valid = arc_pgu_mode_valid,
2363ade7a69SDaniel Vetter 	.enable	= arc_pgu_enable,
2373ade7a69SDaniel Vetter 	.disable = arc_pgu_disable,
2383ade7a69SDaniel Vetter };
2393ade7a69SDaniel Vetter 
2403ade7a69SDaniel Vetter static const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
2413ade7a69SDaniel Vetter 	.fb_create  = drm_gem_fb_create,
2423ade7a69SDaniel Vetter 	.atomic_check = drm_atomic_helper_check,
2433ade7a69SDaniel Vetter 	.atomic_commit = drm_atomic_helper_commit,
2443ade7a69SDaniel Vetter };
2453ade7a69SDaniel Vetter 
2464a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(arcpgu_drm_ops);
2473ade7a69SDaniel Vetter 
arcpgu_load(struct arcpgu_drm_private * arcpgu)2483ade7a69SDaniel Vetter static int arcpgu_load(struct arcpgu_drm_private *arcpgu)
2493ade7a69SDaniel Vetter {
2503ade7a69SDaniel Vetter 	struct platform_device *pdev = to_platform_device(arcpgu->drm.dev);
2513ade7a69SDaniel Vetter 	struct device_node *encoder_node = NULL, *endpoint_node = NULL;
2523ade7a69SDaniel Vetter 	struct drm_connector *connector = NULL;
2533ade7a69SDaniel Vetter 	struct drm_device *drm = &arcpgu->drm;
2543ade7a69SDaniel Vetter 	struct resource *res;
2553ade7a69SDaniel Vetter 	int ret;
2563ade7a69SDaniel Vetter 
2573ade7a69SDaniel Vetter 	arcpgu->clk = devm_clk_get(drm->dev, "pxlclk");
2583ade7a69SDaniel Vetter 	if (IS_ERR(arcpgu->clk))
2593ade7a69SDaniel Vetter 		return PTR_ERR(arcpgu->clk);
2603ade7a69SDaniel Vetter 
2613ade7a69SDaniel Vetter 	ret = drmm_mode_config_init(drm);
2623ade7a69SDaniel Vetter 	if (ret)
2633ade7a69SDaniel Vetter 		return ret;
2643ade7a69SDaniel Vetter 
2653ade7a69SDaniel Vetter 	drm->mode_config.min_width = 0;
2663ade7a69SDaniel Vetter 	drm->mode_config.min_height = 0;
2673ade7a69SDaniel Vetter 	drm->mode_config.max_width = 1920;
2683ade7a69SDaniel Vetter 	drm->mode_config.max_height = 1080;
2693ade7a69SDaniel Vetter 	drm->mode_config.funcs = &arcpgu_drm_modecfg_funcs;
2703ade7a69SDaniel Vetter 
2713ade7a69SDaniel Vetter 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2723ade7a69SDaniel Vetter 	arcpgu->regs = devm_ioremap_resource(&pdev->dev, res);
2733ade7a69SDaniel Vetter 	if (IS_ERR(arcpgu->regs))
2743ade7a69SDaniel Vetter 		return PTR_ERR(arcpgu->regs);
2753ade7a69SDaniel Vetter 
2763ade7a69SDaniel Vetter 	dev_info(drm->dev, "arc_pgu ID: 0x%x\n",
2773ade7a69SDaniel Vetter 		 arc_pgu_read(arcpgu, ARCPGU_REG_ID));
2783ade7a69SDaniel Vetter 
2793ade7a69SDaniel Vetter 	/* Get the optional framebuffer memory resource */
2803ade7a69SDaniel Vetter 	ret = of_reserved_mem_device_init(drm->dev);
2813ade7a69SDaniel Vetter 	if (ret && ret != -ENODEV)
2823ade7a69SDaniel Vetter 		return ret;
2833ade7a69SDaniel Vetter 
2843ade7a69SDaniel Vetter 	if (dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)))
2853ade7a69SDaniel Vetter 		return -ENODEV;
2863ade7a69SDaniel Vetter 
2873ade7a69SDaniel Vetter 	/*
2883ade7a69SDaniel Vetter 	 * There is only one output port inside each device. It is linked with
2893ade7a69SDaniel Vetter 	 * encoder endpoint.
2903ade7a69SDaniel Vetter 	 */
2913ade7a69SDaniel Vetter 	endpoint_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
2923ade7a69SDaniel Vetter 	if (endpoint_node) {
2933ade7a69SDaniel Vetter 		encoder_node = of_graph_get_remote_port_parent(endpoint_node);
2943ade7a69SDaniel Vetter 		of_node_put(endpoint_node);
2953ade7a69SDaniel Vetter 	} else {
2963ade7a69SDaniel Vetter 		connector = &arcpgu->sim_conn;
2973ade7a69SDaniel Vetter 		dev_info(drm->dev, "no encoder found. Assumed virtual LCD on simulation platform\n");
2983ade7a69SDaniel Vetter 		ret = arcpgu_drm_sim_init(drm, connector);
2993ade7a69SDaniel Vetter 		if (ret < 0)
3003ade7a69SDaniel Vetter 			return ret;
3013ade7a69SDaniel Vetter 	}
3023ade7a69SDaniel Vetter 
3033ade7a69SDaniel Vetter 	ret = drm_simple_display_pipe_init(drm, &arcpgu->pipe, &arc_pgu_pipe_funcs,
3043ade7a69SDaniel Vetter 					   arc_pgu_supported_formats,
3053ade7a69SDaniel Vetter 					   ARRAY_SIZE(arc_pgu_supported_formats),
3063ade7a69SDaniel Vetter 					   NULL, connector);
3073ade7a69SDaniel Vetter 	if (ret)
3083ade7a69SDaniel Vetter 		return ret;
3093ade7a69SDaniel Vetter 
3103ade7a69SDaniel Vetter 	if (encoder_node) {
3113ade7a69SDaniel Vetter 		struct drm_bridge *bridge;
3123ade7a69SDaniel Vetter 
3133ade7a69SDaniel Vetter 		/* Locate drm bridge from the hdmi encoder DT node */
3143ade7a69SDaniel Vetter 		bridge = of_drm_find_bridge(encoder_node);
3153ade7a69SDaniel Vetter 		if (!bridge)
3163ade7a69SDaniel Vetter 			return -EPROBE_DEFER;
3173ade7a69SDaniel Vetter 
3183ade7a69SDaniel Vetter 		ret = drm_simple_display_pipe_attach_bridge(&arcpgu->pipe, bridge);
3193ade7a69SDaniel Vetter 		if (ret)
3203ade7a69SDaniel Vetter 			return ret;
3213ade7a69SDaniel Vetter 	}
3223ade7a69SDaniel Vetter 
3233ade7a69SDaniel Vetter 	drm_mode_config_reset(drm);
3243ade7a69SDaniel Vetter 	drm_kms_helper_poll_init(drm);
3253ade7a69SDaniel Vetter 
3263ade7a69SDaniel Vetter 	platform_set_drvdata(pdev, drm);
3273ade7a69SDaniel Vetter 	return 0;
3283ade7a69SDaniel Vetter }
3293ade7a69SDaniel Vetter 
arcpgu_unload(struct drm_device * drm)3303ade7a69SDaniel Vetter static int arcpgu_unload(struct drm_device *drm)
3313ade7a69SDaniel Vetter {
3323ade7a69SDaniel Vetter 	drm_kms_helper_poll_fini(drm);
3333ade7a69SDaniel Vetter 	drm_atomic_helper_shutdown(drm);
3343ade7a69SDaniel Vetter 
3353ade7a69SDaniel Vetter 	return 0;
3363ade7a69SDaniel Vetter }
3373ade7a69SDaniel Vetter 
3383ade7a69SDaniel Vetter #ifdef CONFIG_DEBUG_FS
arcpgu_show_pxlclock(struct seq_file * m,void * arg)3393ade7a69SDaniel Vetter static int arcpgu_show_pxlclock(struct seq_file *m, void *arg)
3403ade7a69SDaniel Vetter {
3413ade7a69SDaniel Vetter 	struct drm_info_node *node = (struct drm_info_node *)m->private;
3423ade7a69SDaniel Vetter 	struct drm_device *drm = node->minor->dev;
3433ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu = dev_to_arcpgu(drm);
3443ade7a69SDaniel Vetter 	unsigned long clkrate = clk_get_rate(arcpgu->clk);
3453ade7a69SDaniel Vetter 	unsigned long mode_clock = arcpgu->pipe.crtc.mode.crtc_clock * 1000;
3463ade7a69SDaniel Vetter 
3473ade7a69SDaniel Vetter 	seq_printf(m, "hw  : %lu\n", clkrate);
3483ade7a69SDaniel Vetter 	seq_printf(m, "mode: %lu\n", mode_clock);
3493ade7a69SDaniel Vetter 	return 0;
3503ade7a69SDaniel Vetter }
3513ade7a69SDaniel Vetter 
3523ade7a69SDaniel Vetter static struct drm_info_list arcpgu_debugfs_list[] = {
3533ade7a69SDaniel Vetter 	{ "clocks", arcpgu_show_pxlclock, 0 },
3543ade7a69SDaniel Vetter };
3553ade7a69SDaniel Vetter 
arcpgu_debugfs_init(struct drm_minor * minor)3563ade7a69SDaniel Vetter static void arcpgu_debugfs_init(struct drm_minor *minor)
3573ade7a69SDaniel Vetter {
3583ade7a69SDaniel Vetter 	drm_debugfs_create_files(arcpgu_debugfs_list,
3593ade7a69SDaniel Vetter 				 ARRAY_SIZE(arcpgu_debugfs_list),
3603ade7a69SDaniel Vetter 				 minor->debugfs_root, minor);
3613ade7a69SDaniel Vetter }
3623ade7a69SDaniel Vetter #endif
3633ade7a69SDaniel Vetter 
3643ade7a69SDaniel Vetter static const struct drm_driver arcpgu_drm_driver = {
3653ade7a69SDaniel Vetter 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
3663ade7a69SDaniel Vetter 	.name = "arcpgu",
3673ade7a69SDaniel Vetter 	.desc = "ARC PGU Controller",
3683ade7a69SDaniel Vetter 	.date = "20160219",
3693ade7a69SDaniel Vetter 	.major = 1,
3703ade7a69SDaniel Vetter 	.minor = 0,
3713ade7a69SDaniel Vetter 	.patchlevel = 0,
3723ade7a69SDaniel Vetter 	.fops = &arcpgu_drm_ops,
3734a83c26aSDanilo Krummrich 	DRM_GEM_DMA_DRIVER_OPS,
3743ade7a69SDaniel Vetter #ifdef CONFIG_DEBUG_FS
3753ade7a69SDaniel Vetter 	.debugfs_init = arcpgu_debugfs_init,
3763ade7a69SDaniel Vetter #endif
3773ade7a69SDaniel Vetter };
3783ade7a69SDaniel Vetter 
arcpgu_probe(struct platform_device * pdev)3793ade7a69SDaniel Vetter static int arcpgu_probe(struct platform_device *pdev)
3803ade7a69SDaniel Vetter {
3813ade7a69SDaniel Vetter 	struct arcpgu_drm_private *arcpgu;
3823ade7a69SDaniel Vetter 	int ret;
3833ade7a69SDaniel Vetter 
3843ade7a69SDaniel Vetter 	arcpgu = devm_drm_dev_alloc(&pdev->dev, &arcpgu_drm_driver,
3853ade7a69SDaniel Vetter 				    struct arcpgu_drm_private, drm);
3863ade7a69SDaniel Vetter 	if (IS_ERR(arcpgu))
3873ade7a69SDaniel Vetter 		return PTR_ERR(arcpgu);
3883ade7a69SDaniel Vetter 
3893ade7a69SDaniel Vetter 	ret = arcpgu_load(arcpgu);
3903ade7a69SDaniel Vetter 	if (ret)
3913ade7a69SDaniel Vetter 		return ret;
3923ade7a69SDaniel Vetter 
3933ade7a69SDaniel Vetter 	ret = drm_dev_register(&arcpgu->drm, 0);
3943ade7a69SDaniel Vetter 	if (ret)
3953ade7a69SDaniel Vetter 		goto err_unload;
3963ade7a69SDaniel Vetter 
397*4144334aSThomas Zimmermann 	drm_fbdev_dma_setup(&arcpgu->drm, 16);
3983ade7a69SDaniel Vetter 
3993ade7a69SDaniel Vetter 	return 0;
4003ade7a69SDaniel Vetter 
4013ade7a69SDaniel Vetter err_unload:
4023ade7a69SDaniel Vetter 	arcpgu_unload(&arcpgu->drm);
4033ade7a69SDaniel Vetter 
4043ade7a69SDaniel Vetter 	return ret;
4053ade7a69SDaniel Vetter }
4063ade7a69SDaniel Vetter 
arcpgu_remove(struct platform_device * pdev)4073ade7a69SDaniel Vetter static int arcpgu_remove(struct platform_device *pdev)
4083ade7a69SDaniel Vetter {
4093ade7a69SDaniel Vetter 	struct drm_device *drm = platform_get_drvdata(pdev);
4103ade7a69SDaniel Vetter 
4113ade7a69SDaniel Vetter 	drm_dev_unregister(drm);
4123ade7a69SDaniel Vetter 	arcpgu_unload(drm);
4133ade7a69SDaniel Vetter 
4143ade7a69SDaniel Vetter 	return 0;
4153ade7a69SDaniel Vetter }
4163ade7a69SDaniel Vetter 
4173ade7a69SDaniel Vetter static const struct of_device_id arcpgu_of_table[] = {
4183ade7a69SDaniel Vetter 	{.compatible = "snps,arcpgu"},
4193ade7a69SDaniel Vetter 	{}
4203ade7a69SDaniel Vetter };
4213ade7a69SDaniel Vetter 
4223ade7a69SDaniel Vetter MODULE_DEVICE_TABLE(of, arcpgu_of_table);
4233ade7a69SDaniel Vetter 
4243ade7a69SDaniel Vetter static struct platform_driver arcpgu_platform_driver = {
4253ade7a69SDaniel Vetter 	.probe = arcpgu_probe,
4263ade7a69SDaniel Vetter 	.remove = arcpgu_remove,
4273ade7a69SDaniel Vetter 	.driver = {
4283ade7a69SDaniel Vetter 		   .name = "arcpgu",
4293ade7a69SDaniel Vetter 		   .of_match_table = arcpgu_of_table,
4303ade7a69SDaniel Vetter 		   },
4313ade7a69SDaniel Vetter };
4323ade7a69SDaniel Vetter 
4338acd15a0SJavier Martinez Canillas drm_module_platform_driver(arcpgu_platform_driver);
4343ade7a69SDaniel Vetter 
4353ade7a69SDaniel Vetter MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>");
4363ade7a69SDaniel Vetter MODULE_DESCRIPTION("ARC PGU DRM driver");
4373ade7a69SDaniel Vetter MODULE_LICENSE("GPL");
438