brintos

brintos / linux-shallow public Read only

0
0
Text · 29.1 KiB · e89eb96 Raw
1042 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2015 Free Electrons4 * Copyright (C) 2015 NextThing Co5 *6 * Maxime Ripard <maxime.ripard@free-electrons.com>7 */8 9#include <linux/component.h>10#include <linux/list.h>11#include <linux/module.h>12#include <linux/of_device.h>13#include <linux/of_graph.h>14#include <linux/dma-mapping.h>15#include <linux/platform_device.h>16#include <linux/reset.h>17 18#include <drm/drm_atomic.h>19#include <drm/drm_atomic_helper.h>20#include <drm/drm_blend.h>21#include <drm/drm_crtc.h>22#include <drm/drm_fb_dma_helper.h>23#include <drm/drm_fourcc.h>24#include <drm/drm_framebuffer.h>25#include <drm/drm_gem_dma_helper.h>26#include <drm/drm_probe_helper.h>27 28#include "sun4i_backend.h"29#include "sun4i_drv.h"30#include "sun4i_frontend.h"31#include "sun4i_layer.h"32#include "sunxi_engine.h"33 34struct sun4i_backend_quirks {35	/* backend <-> TCON muxing selection done in backend */36	bool needs_output_muxing;37 38	/* alpha at the lowest z position is not always supported */39	bool supports_lowest_plane_alpha;40};41 42static const u32 sunxi_rgb2yuv_coef[12] = {43	0x00000107, 0x00000204, 0x00000064, 0x00000108,44	0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,45	0x000001c1, 0x00003e88, 0x00003fb8, 0x0000080846};47 48static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)49{50	int i;51 52	DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");53 54	/* Set color correction */55	regmap_write(engine->regs, SUN4I_BACKEND_OCCTL_REG,56		     SUN4I_BACKEND_OCCTL_ENABLE);57 58	for (i = 0; i < 12; i++)59		regmap_write(engine->regs, SUN4I_BACKEND_OCRCOEF_REG(i),60			     sunxi_rgb2yuv_coef[i]);61}62 63static void sun4i_backend_disable_color_correction(struct sunxi_engine *engine)64{65	DRM_DEBUG_DRIVER("Disabling color correction\n");66 67	/* Disable color correction */68	regmap_update_bits(engine->regs, SUN4I_BACKEND_OCCTL_REG,69			   SUN4I_BACKEND_OCCTL_ENABLE, 0);70}71 72static void sun4i_backend_commit(struct sunxi_engine *engine,73				 struct drm_crtc *crtc,74				 struct drm_atomic_state *state)75{76	DRM_DEBUG_DRIVER("Committing changes\n");77 78	regmap_write(engine->regs, SUN4I_BACKEND_REGBUFFCTL_REG,79		     SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |80		     SUN4I_BACKEND_REGBUFFCTL_LOADCTL);81}82 83void sun4i_backend_layer_enable(struct sun4i_backend *backend,84				int layer, bool enable)85{86	u32 val;87 88	DRM_DEBUG_DRIVER("%sabling layer %d\n", enable ? "En" : "Dis",89			 layer);90 91	if (enable)92		val = SUN4I_BACKEND_MODCTL_LAY_EN(layer);93	else94		val = 0;95 96	regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,97			   SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);98}99 100static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode)101{102	switch (format) {103	case DRM_FORMAT_ARGB8888:104		*mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;105		break;106 107	case DRM_FORMAT_ARGB4444:108		*mode = SUN4I_BACKEND_LAY_FBFMT_ARGB4444;109		break;110 111	case DRM_FORMAT_ARGB1555:112		*mode = SUN4I_BACKEND_LAY_FBFMT_ARGB1555;113		break;114 115	case DRM_FORMAT_RGBA5551:116		*mode = SUN4I_BACKEND_LAY_FBFMT_RGBA5551;117		break;118 119	case DRM_FORMAT_RGBA4444:120		*mode = SUN4I_BACKEND_LAY_FBFMT_RGBA4444;121		break;122 123	case DRM_FORMAT_XRGB8888:124		*mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888;125		break;126 127	case DRM_FORMAT_RGB888:128		*mode = SUN4I_BACKEND_LAY_FBFMT_RGB888;129		break;130 131	case DRM_FORMAT_RGB565:132		*mode = SUN4I_BACKEND_LAY_FBFMT_RGB565;133		break;134 135	default:136		return -EINVAL;137	}138 139	return 0;140}141 142static const uint32_t sun4i_backend_formats[] = {143	DRM_FORMAT_ARGB1555,144	DRM_FORMAT_ARGB4444,145	DRM_FORMAT_ARGB8888,146	DRM_FORMAT_RGB565,147	DRM_FORMAT_RGB888,148	DRM_FORMAT_RGBA4444,149	DRM_FORMAT_RGBA5551,150	DRM_FORMAT_UYVY,151	DRM_FORMAT_VYUY,152	DRM_FORMAT_XRGB8888,153	DRM_FORMAT_YUYV,154	DRM_FORMAT_YVYU,155};156 157bool sun4i_backend_format_is_supported(uint32_t fmt, uint64_t modifier)158{159	unsigned int i;160 161	if (modifier != DRM_FORMAT_MOD_LINEAR)162		return false;163 164	for (i = 0; i < ARRAY_SIZE(sun4i_backend_formats); i++)165		if (sun4i_backend_formats[i] == fmt)166			return true;167 168	return false;169}170 171int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,172				     int layer, struct drm_plane *plane)173{174	struct drm_plane_state *state = plane->state;175 176	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);177 178	/* Set height and width */179	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",180			 state->crtc_w, state->crtc_h);181	regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYSIZE_REG(layer),182		     SUN4I_BACKEND_LAYSIZE(state->crtc_w,183					   state->crtc_h));184 185	/* Set base coordinates */186	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",187			 state->crtc_x, state->crtc_y);188	regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYCOOR_REG(layer),189		     SUN4I_BACKEND_LAYCOOR(state->crtc_x,190					   state->crtc_y));191 192	return 0;193}194 195static int sun4i_backend_update_yuv_format(struct sun4i_backend *backend,196					   int layer, struct drm_plane *plane)197{198	struct drm_plane_state *state = plane->state;199	struct drm_framebuffer *fb = state->fb;200	const struct drm_format_info *format = fb->format;201	const uint32_t fmt = format->format;202	u32 val = SUN4I_BACKEND_IYUVCTL_EN;203	int i;204 205	for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++)206		regmap_write(backend->engine.regs,207			     SUN4I_BACKEND_YGCOEF_REG(i),208			     sunxi_bt601_yuv2rgb_coef[i]);209 210	/*211	 * We should do that only for a single plane, but the212	 * framebuffer's atomic_check has our back on this.213	 */214	regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer),215			   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN,216			   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN);217 218	/* TODO: Add support for the multi-planar YUV formats */219	if (drm_format_info_is_yuv_packed(format) &&220	    drm_format_info_is_yuv_sampling_422(format))221		val |= SUN4I_BACKEND_IYUVCTL_FBFMT_PACKED_YUV422;222	else223		DRM_DEBUG_DRIVER("Unsupported YUV format (0x%x)\n", fmt);224 225	/*226	 * Allwinner seems to list the pixel sequence from right to left, while227	 * DRM lists it from left to right.228	 */229	switch (fmt) {230	case DRM_FORMAT_YUYV:231		val |= SUN4I_BACKEND_IYUVCTL_FBPS_VYUY;232		break;233	case DRM_FORMAT_YVYU:234		val |= SUN4I_BACKEND_IYUVCTL_FBPS_UYVY;235		break;236	case DRM_FORMAT_UYVY:237		val |= SUN4I_BACKEND_IYUVCTL_FBPS_YVYU;238		break;239	case DRM_FORMAT_VYUY:240		val |= SUN4I_BACKEND_IYUVCTL_FBPS_YUYV;241		break;242	default:243		DRM_DEBUG_DRIVER("Unsupported YUV pixel sequence (0x%x)\n",244				 fmt);245	}246 247	regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVCTL_REG, val);248 249	return 0;250}251 252int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,253				       int layer, struct drm_plane *plane)254{255	struct drm_plane_state *state = plane->state;256	struct drm_framebuffer *fb = state->fb;257	u32 val;258	int ret;259 260	/* Clear the YUV mode */261	regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer),262			   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0);263 264	val = SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA(state->alpha >> 8);265	if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)266		val |= SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN;267	regmap_update_bits(backend->engine.regs,268			   SUN4I_BACKEND_ATTCTL_REG0(layer),269			   SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_MASK |270			   SUN4I_BACKEND_ATTCTL_REG0_LAY_GLBALPHA_EN,271			   val);272 273	if (fb->format->is_yuv)274		return sun4i_backend_update_yuv_format(backend, layer, plane);275 276	ret = sun4i_backend_drm_format_to_layer(fb->format->format, &val);277	if (ret) {278		DRM_DEBUG_DRIVER("Invalid format\n");279		return ret;280	}281 282	regmap_update_bits(backend->engine.regs,283			   SUN4I_BACKEND_ATTCTL_REG1(layer),284			   SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);285 286	return 0;287}288 289int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend,290					int layer, uint32_t fmt)291{292	u32 val;293	int ret;294 295	ret = sun4i_backend_drm_format_to_layer(fmt, &val);296	if (ret) {297		DRM_DEBUG_DRIVER("Invalid format\n");298		return ret;299	}300 301	regmap_update_bits(backend->engine.regs,302			   SUN4I_BACKEND_ATTCTL_REG0(layer),303			   SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN,304			   SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN);305 306	regmap_update_bits(backend->engine.regs,307			   SUN4I_BACKEND_ATTCTL_REG1(layer),308			   SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);309 310	return 0;311}312 313static int sun4i_backend_update_yuv_buffer(struct sun4i_backend *backend,314					   struct drm_framebuffer *fb,315					   dma_addr_t paddr)316{317	/* TODO: Add support for the multi-planar YUV formats */318	DRM_DEBUG_DRIVER("Setting packed YUV buffer address to %pad\n", &paddr);319	regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVADD_REG(0), paddr);320 321	DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);322	regmap_write(backend->engine.regs, SUN4I_BACKEND_IYUVLINEWIDTH_REG(0),323		     fb->pitches[0] * 8);324 325	return 0;326}327 328int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,329				      int layer, struct drm_plane *plane)330{331	struct drm_plane_state *state = plane->state;332	struct drm_framebuffer *fb = state->fb;333	u32 lo_paddr, hi_paddr;334	dma_addr_t dma_addr;335 336	/* Set the line width */337	DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);338	regmap_write(backend->engine.regs,339		     SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),340		     fb->pitches[0] * 8);341 342	/* Get the start of the displayed memory */343	dma_addr = drm_fb_dma_get_gem_addr(fb, state, 0);344	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr);345 346	if (fb->format->is_yuv)347		return sun4i_backend_update_yuv_buffer(backend, fb, dma_addr);348 349	/* Write the 32 lower bits of the address (in bits) */350	lo_paddr = dma_addr << 3;351	DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);352	regmap_write(backend->engine.regs,353		     SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),354		     lo_paddr);355 356	/* And the upper bits */357	hi_paddr = dma_addr >> 29;358	DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);359	regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,360			   SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),361			   SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));362 363	return 0;364}365 366int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer,367				    struct drm_plane *plane)368{369	struct drm_plane_state *state = plane->state;370	struct sun4i_layer_state *p_state = state_to_sun4i_layer_state(state);371	unsigned int priority = state->normalized_zpos;372	unsigned int pipe = p_state->pipe;373 374	DRM_DEBUG_DRIVER("Setting layer %d's priority to %d and pipe %d\n",375			 layer, priority, pipe);376	regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer),377			   SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK |378			   SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK,379			   SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(p_state->pipe) |380			   SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(priority));381 382	return 0;383}384 385void sun4i_backend_cleanup_layer(struct sun4i_backend *backend,386				 int layer)387{388	regmap_update_bits(backend->engine.regs,389			   SUN4I_BACKEND_ATTCTL_REG0(layer),390			   SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN |391			   SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0);392}393 394static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state)395{396	u16 src_h = state->src_h >> 16;397	u16 src_w = state->src_w >> 16;398 399	DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n",400			 src_w, src_h, state->crtc_w, state->crtc_h);401 402	if ((state->crtc_h != src_h) || (state->crtc_w != src_w))403		return true;404 405	return false;406}407 408static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state)409{410	struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane);411	struct sun4i_backend *backend = layer->backend;412	uint32_t format = state->fb->format->format;413	uint64_t modifier = state->fb->modifier;414 415	if (IS_ERR(backend->frontend))416		return false;417 418	if (!sun4i_frontend_format_is_supported(format, modifier))419		return false;420 421	if (!sun4i_backend_format_is_supported(format, modifier))422		return true;423 424	/*425	 * TODO: The backend alone allows 2x and 4x integer scaling, including426	 * support for an alpha component (which the frontend doesn't support).427	 * Use the backend directly instead of the frontend in this case, with428	 * another test to return false.429	 */430 431	if (sun4i_backend_plane_uses_scaler(state))432		return true;433 434	/*435	 * Here the format is supported by both the frontend and the backend436	 * and no frontend scaling is required, so use the backend directly.437	 */438	return false;439}440 441static bool sun4i_backend_plane_is_supported(struct drm_plane_state *state,442					     bool *uses_frontend)443{444	if (sun4i_backend_plane_uses_frontend(state)) {445		*uses_frontend = true;446		return true;447	}448 449	*uses_frontend = false;450 451	/* Scaling is not supported without the frontend. */452	if (sun4i_backend_plane_uses_scaler(state))453		return false;454 455	return true;456}457 458static void sun4i_backend_atomic_begin(struct sunxi_engine *engine,459				       struct drm_crtc_state *old_state)460{461	u32 val;462 463	WARN_ON(regmap_read_poll_timeout(engine->regs,464					 SUN4I_BACKEND_REGBUFFCTL_REG,465					 val, !(val & SUN4I_BACKEND_REGBUFFCTL_LOADCTL),466					 100, 50000));467}468 469static int sun4i_backend_atomic_check(struct sunxi_engine *engine,470				      struct drm_crtc_state *crtc_state)471{472	struct drm_plane_state *plane_states[SUN4I_BACKEND_NUM_LAYERS] = { 0 };473	struct sun4i_backend *backend = engine_to_sun4i_backend(engine);474	struct drm_atomic_state *state = crtc_state->state;475	struct drm_device *drm = state->dev;476	struct drm_plane *plane;477	unsigned int num_planes = 0;478	unsigned int num_alpha_planes = 0;479	unsigned int num_frontend_planes = 0;480	unsigned int num_alpha_planes_max = 1;481	unsigned int num_yuv_planes = 0;482	unsigned int current_pipe = 0;483	unsigned int i;484 485	DRM_DEBUG_DRIVER("Starting checking our planes\n");486 487	if (!crtc_state->planes_changed)488		return 0;489 490	drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) {491		struct drm_plane_state *plane_state =492			drm_atomic_get_plane_state(state, plane);493		struct sun4i_layer_state *layer_state =494			state_to_sun4i_layer_state(plane_state);495		struct drm_framebuffer *fb = plane_state->fb;496 497		if (!sun4i_backend_plane_is_supported(plane_state,498						      &layer_state->uses_frontend))499			return -EINVAL;500 501		if (layer_state->uses_frontend) {502			DRM_DEBUG_DRIVER("Using the frontend for plane %d\n",503					 plane->index);504			num_frontend_planes++;505		} else {506			if (fb->format->is_yuv) {507				DRM_DEBUG_DRIVER("Plane FB format is YUV\n");508				num_yuv_planes++;509			}510		}511 512		DRM_DEBUG_DRIVER("Plane FB format is %p4cc\n",513				 &fb->format->format);514		if (fb->format->has_alpha || (plane_state->alpha != DRM_BLEND_ALPHA_OPAQUE))515			num_alpha_planes++;516 517		DRM_DEBUG_DRIVER("Plane zpos is %d\n",518				 plane_state->normalized_zpos);519 520		/* Sort our planes by Zpos */521		plane_states[plane_state->normalized_zpos] = plane_state;522 523		num_planes++;524	}525 526	/* All our planes were disabled, bail out */527	if (!num_planes)528		return 0;529 530	/*531	 * The hardware is a bit unusual here.532	 *533	 * Even though it supports 4 layers, it does the composition534	 * in two separate steps.535	 *536	 * The first one is assigning a layer to one of its two537	 * pipes. If more that 1 layer is assigned to the same pipe,538	 * and if pixels overlaps, the pipe will take the pixel from539	 * the layer with the highest priority.540	 *541	 * The second step is the actual alpha blending, that takes542	 * the two pipes as input, and uses the potential alpha543	 * component to do the transparency between the two.544	 *545	 * This two-step scenario makes us unable to guarantee a546	 * robust alpha blending between the 4 layers in all547	 * situations, since this means that we need to have one layer548	 * with alpha at the lowest position of our two pipes.549	 *550	 * However, we cannot even do that on every platform, since551	 * the hardware has a bug where the lowest plane of the lowest552	 * pipe (pipe 0, priority 0), if it has any alpha, will553	 * discard the pixel data entirely and just display the pixels554	 * in the background color (black by default).555	 *556	 * This means that on the affected platforms, we effectively557	 * have only three valid configurations with alpha, all of558	 * them with the alpha being on pipe1 with the lowest559	 * position, which can be 1, 2 or 3 depending on the number of560	 * planes and their zpos.561	 */562 563	/* For platforms that are not affected by the issue described above. */564	if (backend->quirks->supports_lowest_plane_alpha)565		num_alpha_planes_max++;566 567	if (num_alpha_planes > num_alpha_planes_max) {568		DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n");569		return -EINVAL;570	}571 572	/* We can't have an alpha plane at the lowest position */573	if (!backend->quirks->supports_lowest_plane_alpha &&574	    (plane_states[0]->alpha != DRM_BLEND_ALPHA_OPAQUE))575		return -EINVAL;576 577	for (i = 1; i < num_planes; i++) {578		struct drm_plane_state *p_state = plane_states[i];579		struct drm_framebuffer *fb = p_state->fb;580		struct sun4i_layer_state *s_state = state_to_sun4i_layer_state(p_state);581 582		/*583		 * The only alpha position is the lowest plane of the584		 * second pipe.585		 */586		if (fb->format->has_alpha || (p_state->alpha != DRM_BLEND_ALPHA_OPAQUE))587			current_pipe++;588 589		s_state->pipe = current_pipe;590	}591 592	/* We can only have a single YUV plane at a time */593	if (num_yuv_planes > SUN4I_BACKEND_NUM_YUV_PLANES) {594		DRM_DEBUG_DRIVER("Too many planes with YUV, rejecting...\n");595		return -EINVAL;596	}597 598	if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) {599		DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n");600		return -EINVAL;601	}602 603	DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video, %u YUV\n",604			 num_planes, num_alpha_planes, num_frontend_planes,605			 num_yuv_planes);606 607	return 0;608}609 610static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine)611{612	struct sun4i_backend *backend = engine_to_sun4i_backend(engine);613	struct sun4i_frontend *frontend = backend->frontend;614 615	if (!frontend)616		return;617 618	/*619	 * In a teardown scenario with the frontend involved, we have620	 * to keep the frontend enabled until the next vblank, and621	 * only then disable it.622	 *623	 * This is due to the fact that the backend will not take into624	 * account the new configuration (with the plane that used to625	 * be fed by the frontend now disabled) until we write to the626	 * commit bit and the hardware fetches the new configuration627	 * during the next vblank.628	 *629	 * So we keep the frontend around in order to prevent any630	 * visual artifacts.631	 */632	spin_lock(&backend->frontend_lock);633	if (backend->frontend_teardown) {634		sun4i_frontend_exit(frontend);635		backend->frontend_teardown = false;636	}637	spin_unlock(&backend->frontend_lock);638};639 640static void sun4i_backend_mode_set(struct sunxi_engine *engine,641				   const struct drm_display_mode *mode)642{643	bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);644 645	DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n",646			 mode->hdisplay, mode->vdisplay);647 648	regmap_write(engine->regs, SUN4I_BACKEND_DISSIZE_REG,649		     SUN4I_BACKEND_DISSIZE(mode->hdisplay, mode->vdisplay));650 651	regmap_update_bits(engine->regs, SUN4I_BACKEND_MODCTL_REG,652			   SUN4I_BACKEND_MODCTL_ITLMOD_EN,653			   interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);654 655	DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",656			 interlaced ? "on" : "off");657}658 659static int sun4i_backend_init_sat(struct device *dev) {660	struct sun4i_backend *backend = dev_get_drvdata(dev);661	int ret;662 663	backend->sat_reset = devm_reset_control_get(dev, "sat");664	if (IS_ERR(backend->sat_reset)) {665		dev_err(dev, "Couldn't get the SAT reset line\n");666		return PTR_ERR(backend->sat_reset);667	}668 669	ret = reset_control_deassert(backend->sat_reset);670	if (ret) {671		dev_err(dev, "Couldn't deassert the SAT reset line\n");672		return ret;673	}674 675	backend->sat_clk = devm_clk_get(dev, "sat");676	if (IS_ERR(backend->sat_clk)) {677		dev_err(dev, "Couldn't get our SAT clock\n");678		ret = PTR_ERR(backend->sat_clk);679		goto err_assert_reset;680	}681 682	ret = clk_prepare_enable(backend->sat_clk);683	if (ret) {684		dev_err(dev, "Couldn't enable the SAT clock\n");685		return ret;686	}687 688	return 0;689 690err_assert_reset:691	reset_control_assert(backend->sat_reset);692	return ret;693}694 695static int sun4i_backend_free_sat(struct device *dev) {696	struct sun4i_backend *backend = dev_get_drvdata(dev);697 698	clk_disable_unprepare(backend->sat_clk);699	reset_control_assert(backend->sat_reset);700 701	return 0;702}703 704/*705 * The display backend can take video output from the display frontend, or706 * the display enhancement unit on the A80, as input for one it its layers.707 * This relationship within the display pipeline is encoded in the device708 * tree with of_graph, and we use it here to figure out which backend, if709 * there are 2 or more, we are currently probing. The number would be in710 * the "reg" property of the upstream output port endpoint.711 */712static int sun4i_backend_of_get_id(struct device_node *node)713{714	struct device_node *ep, *remote;715	struct of_endpoint of_ep;716 717	/* Input port is 0, and we want the first endpoint. */718	ep = of_graph_get_endpoint_by_regs(node, 0, -1);719	if (!ep)720		return -EINVAL;721 722	remote = of_graph_get_remote_endpoint(ep);723	of_node_put(ep);724	if (!remote)725		return -EINVAL;726 727	of_graph_parse_endpoint(remote, &of_ep);728	of_node_put(remote);729	return of_ep.id;730}731 732/* TODO: This needs to take multiple pipelines into account */733static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv,734							  struct device_node *node)735{736	struct device_node *port, *ep, *remote;737	struct sun4i_frontend *frontend;738 739	port = of_graph_get_port_by_id(node, 0);740	if (!port)741		return ERR_PTR(-EINVAL);742 743	for_each_available_child_of_node(port, ep) {744		remote = of_graph_get_remote_port_parent(ep);745		if (!remote)746			continue;747		of_node_put(remote);748 749		/* does this node match any registered engines? */750		list_for_each_entry(frontend, &drv->frontend_list, list) {751			if (remote == frontend->node) {752				of_node_put(port);753				of_node_put(ep);754				return frontend;755			}756		}757	}758	of_node_put(port);759	return ERR_PTR(-EINVAL);760}761 762static const struct sunxi_engine_ops sun4i_backend_engine_ops = {763	.atomic_begin			= sun4i_backend_atomic_begin,764	.atomic_check			= sun4i_backend_atomic_check,765	.commit				= sun4i_backend_commit,766	.layers_init			= sun4i_layers_init,767	.apply_color_correction		= sun4i_backend_apply_color_correction,768	.disable_color_correction	= sun4i_backend_disable_color_correction,769	.vblank_quirk			= sun4i_backend_vblank_quirk,770	.mode_set			= sun4i_backend_mode_set,771};772 773static const struct regmap_config sun4i_backend_regmap_config = {774	.reg_bits	= 32,775	.val_bits	= 32,776	.reg_stride	= 4,777	.max_register	= 0x5800,778};779 780static int sun4i_backend_bind(struct device *dev, struct device *master,781			      void *data)782{783	struct platform_device *pdev = to_platform_device(dev);784	struct drm_device *drm = data;785	struct sun4i_drv *drv = drm->dev_private;786	struct sun4i_backend *backend;787	const struct sun4i_backend_quirks *quirks;788	void __iomem *regs;789	int i, ret;790 791	backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL);792	if (!backend)793		return -ENOMEM;794	dev_set_drvdata(dev, backend);795	spin_lock_init(&backend->frontend_lock);796 797	if (of_property_present(dev->of_node, "interconnects")) {798		/*799		 * This assume we have the same DMA constraints for all our the800		 * devices in our pipeline (all the backends, but also the801		 * frontends). This sounds bad, but it has always been the case802		 * for us, and DRM doesn't do per-device allocation either, so803		 * we would need to fix DRM first...804		 */805		ret = of_dma_configure(drm->dev, dev->of_node, true);806		if (ret)807			return ret;808	}809 810	backend->engine.node = dev->of_node;811	backend->engine.ops = &sun4i_backend_engine_ops;812	backend->engine.id = sun4i_backend_of_get_id(dev->of_node);813	if (backend->engine.id < 0)814		return backend->engine.id;815 816	backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node);817	if (IS_ERR(backend->frontend))818		dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n");819 820	regs = devm_platform_ioremap_resource(pdev, 0);821	if (IS_ERR(regs))822		return PTR_ERR(regs);823 824	backend->reset = devm_reset_control_get(dev, NULL);825	if (IS_ERR(backend->reset)) {826		dev_err(dev, "Couldn't get our reset line\n");827		return PTR_ERR(backend->reset);828	}829 830	ret = reset_control_deassert(backend->reset);831	if (ret) {832		dev_err(dev, "Couldn't deassert our reset line\n");833		return ret;834	}835 836	backend->bus_clk = devm_clk_get(dev, "ahb");837	if (IS_ERR(backend->bus_clk)) {838		dev_err(dev, "Couldn't get the backend bus clock\n");839		ret = PTR_ERR(backend->bus_clk);840		goto err_assert_reset;841	}842	clk_prepare_enable(backend->bus_clk);843 844	backend->mod_clk = devm_clk_get(dev, "mod");845	if (IS_ERR(backend->mod_clk)) {846		dev_err(dev, "Couldn't get the backend module clock\n");847		ret = PTR_ERR(backend->mod_clk);848		goto err_disable_bus_clk;849	}850 851	ret = clk_set_rate_exclusive(backend->mod_clk, 300000000);852	if (ret) {853		dev_err(dev, "Couldn't set the module clock frequency\n");854		goto err_disable_bus_clk;855	}856 857	clk_prepare_enable(backend->mod_clk);858 859	backend->ram_clk = devm_clk_get(dev, "ram");860	if (IS_ERR(backend->ram_clk)) {861		dev_err(dev, "Couldn't get the backend RAM clock\n");862		ret = PTR_ERR(backend->ram_clk);863		goto err_disable_mod_clk;864	}865	clk_prepare_enable(backend->ram_clk);866 867	if (of_device_is_compatible(dev->of_node,868				    "allwinner,sun8i-a33-display-backend")) {869		ret = sun4i_backend_init_sat(dev);870		if (ret) {871			dev_err(dev, "Couldn't init SAT resources\n");872			goto err_disable_ram_clk;873		}874	}875 876	backend->engine.regs = devm_regmap_init_mmio(dev, regs,877						     &sun4i_backend_regmap_config);878	if (IS_ERR(backend->engine.regs)) {879		dev_err(dev, "Couldn't create the backend regmap\n");880		return PTR_ERR(backend->engine.regs);881	}882 883	list_add_tail(&backend->engine.list, &drv->engine_list);884 885	/*886	 * Many of the backend's layer configuration registers have887	 * undefined default values. This poses a risk as we use888	 * regmap_update_bits in some places, and don't overwrite889	 * the whole register.890	 *891	 * Clear the registers here to have something predictable.892	 */893	for (i = 0x800; i < 0x1000; i += 4)894		regmap_write(backend->engine.regs, i, 0);895 896	/* Disable registers autoloading */897	regmap_write(backend->engine.regs, SUN4I_BACKEND_REGBUFFCTL_REG,898		     SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);899 900	/* Enable the backend */901	regmap_write(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,902		     SUN4I_BACKEND_MODCTL_DEBE_EN |903		     SUN4I_BACKEND_MODCTL_START_CTL);904 905	/* Set output selection if needed */906	quirks = of_device_get_match_data(dev);907	if (quirks->needs_output_muxing) {908		/*909		 * We assume there is no dynamic muxing of backends910		 * and TCONs, so we select the backend with same ID.911		 *912		 * While dynamic selection might be interesting, since913		 * the CRTC is tied to the TCON, while the layers are914		 * tied to the backends, this means, we will need to915		 * switch between groups of layers. There might not be916		 * a way to represent this constraint in DRM.917		 */918		regmap_update_bits(backend->engine.regs,919				   SUN4I_BACKEND_MODCTL_REG,920				   SUN4I_BACKEND_MODCTL_OUT_SEL,921				   (backend->engine.id922				    ? SUN4I_BACKEND_MODCTL_OUT_LCD1923				    : SUN4I_BACKEND_MODCTL_OUT_LCD0));924	}925 926	backend->quirks = quirks;927 928	return 0;929 930err_disable_ram_clk:931	clk_disable_unprepare(backend->ram_clk);932err_disable_mod_clk:933	clk_rate_exclusive_put(backend->mod_clk);934	clk_disable_unprepare(backend->mod_clk);935err_disable_bus_clk:936	clk_disable_unprepare(backend->bus_clk);937err_assert_reset:938	reset_control_assert(backend->reset);939	return ret;940}941 942static void sun4i_backend_unbind(struct device *dev, struct device *master,943				 void *data)944{945	struct sun4i_backend *backend = dev_get_drvdata(dev);946 947	list_del(&backend->engine.list);948 949	if (of_device_is_compatible(dev->of_node,950				    "allwinner,sun8i-a33-display-backend"))951		sun4i_backend_free_sat(dev);952 953	clk_disable_unprepare(backend->ram_clk);954	clk_rate_exclusive_put(backend->mod_clk);955	clk_disable_unprepare(backend->mod_clk);956	clk_disable_unprepare(backend->bus_clk);957	reset_control_assert(backend->reset);958}959 960static const struct component_ops sun4i_backend_ops = {961	.bind	= sun4i_backend_bind,962	.unbind	= sun4i_backend_unbind,963};964 965static int sun4i_backend_probe(struct platform_device *pdev)966{967	return component_add(&pdev->dev, &sun4i_backend_ops);968}969 970static void sun4i_backend_remove(struct platform_device *pdev)971{972	component_del(&pdev->dev, &sun4i_backend_ops);973}974 975static const struct sun4i_backend_quirks sun4i_backend_quirks = {976	.needs_output_muxing = true,977};978 979static const struct sun4i_backend_quirks sun5i_backend_quirks = {980};981 982static const struct sun4i_backend_quirks sun6i_backend_quirks = {983};984 985static const struct sun4i_backend_quirks sun7i_backend_quirks = {986	.needs_output_muxing = true,987};988 989static const struct sun4i_backend_quirks sun8i_a33_backend_quirks = {990	.supports_lowest_plane_alpha = true,991};992 993static const struct sun4i_backend_quirks sun9i_backend_quirks = {994};995 996static const struct of_device_id sun4i_backend_of_table[] = {997	{998		.compatible = "allwinner,sun4i-a10-display-backend",999		.data = &sun4i_backend_quirks,1000	},1001	{1002		.compatible = "allwinner,sun5i-a13-display-backend",1003		.data = &sun5i_backend_quirks,1004	},1005	{1006		.compatible = "allwinner,sun6i-a31-display-backend",1007		.data = &sun6i_backend_quirks,1008	},1009	{1010		.compatible = "allwinner,sun7i-a20-display-backend",1011		.data = &sun7i_backend_quirks,1012	},1013	{1014		.compatible = "allwinner,sun8i-a23-display-backend",1015		.data = &sun8i_a33_backend_quirks,1016	},1017	{1018		.compatible = "allwinner,sun8i-a33-display-backend",1019		.data = &sun8i_a33_backend_quirks,1020	},1021	{1022		.compatible = "allwinner,sun9i-a80-display-backend",1023		.data = &sun9i_backend_quirks,1024	},1025	{ }1026};1027MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);1028 1029static struct platform_driver sun4i_backend_platform_driver = {1030	.probe		= sun4i_backend_probe,1031	.remove_new	= sun4i_backend_remove,1032	.driver		= {1033		.name		= "sun4i-backend",1034		.of_match_table	= sun4i_backend_of_table,1035	},1036};1037module_platform_driver(sun4i_backend_platform_driver);1038 1039MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");1040MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver");1041MODULE_LICENSE("GPL");1042