162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
462306a36Sopenharmony_ci * Author: Rob Clark <rob@ti.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/sort.h>
1162306a36Sopenharmony_ci#include <linux/sys_soc.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1562306a36Sopenharmony_ci#include <drm/drm_bridge.h>
1662306a36Sopenharmony_ci#include <drm/drm_bridge_connector.h>
1762306a36Sopenharmony_ci#include <drm/drm_drv.h>
1862306a36Sopenharmony_ci#include <drm/drm_file.h>
1962306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
2062306a36Sopenharmony_ci#include <drm/drm_panel.h>
2162306a36Sopenharmony_ci#include <drm/drm_prime.h>
2262306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
2362306a36Sopenharmony_ci#include <drm/drm_vblank.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "omap_dmm_tiler.h"
2662306a36Sopenharmony_ci#include "omap_drv.h"
2762306a36Sopenharmony_ci#include "omap_fbdev.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DRIVER_NAME		MODULE_NAME
3062306a36Sopenharmony_ci#define DRIVER_DESC		"OMAP DRM"
3162306a36Sopenharmony_ci#define DRIVER_DATE		"20110917"
3262306a36Sopenharmony_ci#define DRIVER_MAJOR		1
3362306a36Sopenharmony_ci#define DRIVER_MINOR		0
3462306a36Sopenharmony_ci#define DRIVER_PATCHLEVEL	0
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * mode config funcs
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Notes about mapping DSS and DRM entities:
4162306a36Sopenharmony_ci *    CRTC:        overlay
4262306a36Sopenharmony_ci *    encoder:     manager.. with some extension to allow one primary CRTC
4362306a36Sopenharmony_ci *                 and zero or more video CRTC's to be mapped to one encoder?
4462306a36Sopenharmony_ci *    connector:   dssdev.. manager can be attached/detached from different
4562306a36Sopenharmony_ci *                 devices
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void omap_atomic_wait_for_completion(struct drm_device *dev,
4962306a36Sopenharmony_ci					    struct drm_atomic_state *old_state)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state;
5262306a36Sopenharmony_ci	struct drm_crtc *crtc;
5362306a36Sopenharmony_ci	unsigned int i;
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
5762306a36Sopenharmony_ci		if (!new_crtc_state->active)
5862306a36Sopenharmony_ci			continue;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		ret = omap_crtc_wait_pending(crtc);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		if (!ret)
6362306a36Sopenharmony_ci			dev_warn(dev->dev,
6462306a36Sopenharmony_ci				 "atomic complete timeout (pipe %u)!\n", i);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void omap_atomic_commit_tail(struct drm_atomic_state *old_state)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct drm_device *dev = old_state->dev;
7162306a36Sopenharmony_ci	struct omap_drm_private *priv = dev->dev_private;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	dispc_runtime_get(priv->dispc);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Apply the atomic update. */
7662306a36Sopenharmony_ci	drm_atomic_helper_commit_modeset_disables(dev, old_state);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (priv->omaprev != 0x3430) {
7962306a36Sopenharmony_ci		/* With the current dss dispc implementation we have to enable
8062306a36Sopenharmony_ci		 * the new modeset before we can commit planes. The dispc ovl
8162306a36Sopenharmony_ci		 * configuration relies on the video mode configuration been
8262306a36Sopenharmony_ci		 * written into the HW when the ovl configuration is
8362306a36Sopenharmony_ci		 * calculated.
8462306a36Sopenharmony_ci		 *
8562306a36Sopenharmony_ci		 * This approach is not ideal because after a mode change the
8662306a36Sopenharmony_ci		 * plane update is executed only after the first vblank
8762306a36Sopenharmony_ci		 * interrupt. The dispc implementation should be fixed so that
8862306a36Sopenharmony_ci		 * it is able use uncommitted drm state information.
8962306a36Sopenharmony_ci		 */
9062306a36Sopenharmony_ci		drm_atomic_helper_commit_modeset_enables(dev, old_state);
9162306a36Sopenharmony_ci		omap_atomic_wait_for_completion(dev, old_state);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		drm_atomic_helper_commit_planes(dev, old_state, 0);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		drm_atomic_helper_commit_hw_done(old_state);
9662306a36Sopenharmony_ci	} else {
9762306a36Sopenharmony_ci		/*
9862306a36Sopenharmony_ci		 * OMAP3 DSS seems to have issues with the work-around above,
9962306a36Sopenharmony_ci		 * resulting in endless sync losts if a crtc is enabled without
10062306a36Sopenharmony_ci		 * a plane. For now, skip the WA for OMAP3.
10162306a36Sopenharmony_ci		 */
10262306a36Sopenharmony_ci		drm_atomic_helper_commit_planes(dev, old_state, 0);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		drm_atomic_helper_commit_modeset_enables(dev, old_state);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		drm_atomic_helper_commit_hw_done(old_state);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/*
11062306a36Sopenharmony_ci	 * Wait for completion of the page flips to ensure that old buffers
11162306a36Sopenharmony_ci	 * can't be touched by the hardware anymore before cleaning up planes.
11262306a36Sopenharmony_ci	 */
11362306a36Sopenharmony_ci	omap_atomic_wait_for_completion(dev, old_state);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	drm_atomic_helper_cleanup_planes(dev, old_state);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dispc_runtime_put(priv->dispc);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int drm_atomic_state_normalized_zpos_cmp(const void *a, const void *b)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	const struct drm_plane_state *sa = *(struct drm_plane_state **)a;
12362306a36Sopenharmony_ci	const struct drm_plane_state *sb = *(struct drm_plane_state **)b;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (sa->normalized_zpos != sb->normalized_zpos)
12662306a36Sopenharmony_ci		return sa->normalized_zpos - sb->normalized_zpos;
12762306a36Sopenharmony_ci	else
12862306a36Sopenharmony_ci		return sa->plane->base.id - sb->plane->base.id;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * This replaces the drm_atomic_normalize_zpos to handle the dual overlay case.
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * Since both halves need to be 'appear' side by side the zpos is
13562306a36Sopenharmony_ci * recalculated when dealing with dual overlay cases so that the other
13662306a36Sopenharmony_ci * planes zpos is consistent.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic int omap_atomic_update_normalize_zpos(struct drm_device *dev,
13962306a36Sopenharmony_ci					     struct drm_atomic_state *state)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct drm_crtc *crtc;
14262306a36Sopenharmony_ci	struct drm_crtc_state *old_state, *new_state;
14362306a36Sopenharmony_ci	struct drm_plane *plane;
14462306a36Sopenharmony_ci	int c, i, n, inc;
14562306a36Sopenharmony_ci	int total_planes = dev->mode_config.num_total_plane;
14662306a36Sopenharmony_ci	struct drm_plane_state **states;
14762306a36Sopenharmony_ci	int ret = 0;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL);
15062306a36Sopenharmony_ci	if (!states)
15162306a36Sopenharmony_ci		return -ENOMEM;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, c) {
15462306a36Sopenharmony_ci		if (old_state->plane_mask == new_state->plane_mask &&
15562306a36Sopenharmony_ci		    !new_state->zpos_changed)
15662306a36Sopenharmony_ci			continue;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		/* Reset plane increment and index value for every crtc */
15962306a36Sopenharmony_ci		n = 0;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		/*
16262306a36Sopenharmony_ci		 * Normalization process might create new states for planes
16362306a36Sopenharmony_ci		 * which normalized_zpos has to be recalculated.
16462306a36Sopenharmony_ci		 */
16562306a36Sopenharmony_ci		drm_for_each_plane_mask(plane, dev, new_state->plane_mask) {
16662306a36Sopenharmony_ci			struct drm_plane_state *plane_state =
16762306a36Sopenharmony_ci				drm_atomic_get_plane_state(new_state->state,
16862306a36Sopenharmony_ci							   plane);
16962306a36Sopenharmony_ci			if (IS_ERR(plane_state)) {
17062306a36Sopenharmony_ci				ret = PTR_ERR(plane_state);
17162306a36Sopenharmony_ci				goto done;
17262306a36Sopenharmony_ci			}
17362306a36Sopenharmony_ci			states[n++] = plane_state;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		sort(states, n, sizeof(*states),
17762306a36Sopenharmony_ci		     drm_atomic_state_normalized_zpos_cmp, NULL);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		for (i = 0, inc = 0; i < n; i++) {
18062306a36Sopenharmony_ci			plane = states[i]->plane;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci			states[i]->normalized_zpos = i + inc;
18362306a36Sopenharmony_ci			DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n",
18462306a36Sopenharmony_ci					 plane->base.id, plane->name,
18562306a36Sopenharmony_ci					 states[i]->normalized_zpos);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci			if (is_omap_plane_dual_overlay(states[i]))
18862306a36Sopenharmony_ci				inc++;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci		new_state->zpos_changed = true;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cidone:
19462306a36Sopenharmony_ci	kfree(states);
19562306a36Sopenharmony_ci	return ret;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int omap_atomic_check(struct drm_device *dev,
19962306a36Sopenharmony_ci			     struct drm_atomic_state *state)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = drm_atomic_helper_check(dev, state);
20462306a36Sopenharmony_ci	if (ret)
20562306a36Sopenharmony_ci		return ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (dev->mode_config.normalize_zpos) {
20862306a36Sopenharmony_ci		ret = omap_atomic_update_normalize_zpos(dev, state);
20962306a36Sopenharmony_ci		if (ret)
21062306a36Sopenharmony_ci			return ret;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = {
21762306a36Sopenharmony_ci	.atomic_commit_tail = omap_atomic_commit_tail,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic const struct drm_mode_config_funcs omap_mode_config_funcs = {
22162306a36Sopenharmony_ci	.fb_create = omap_framebuffer_create,
22262306a36Sopenharmony_ci	.atomic_check = omap_atomic_check,
22362306a36Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* Global/shared object state funcs */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*
22962306a36Sopenharmony_ci * This is a helper that returns the private state currently in operation.
23062306a36Sopenharmony_ci * Note that this would return the "old_state" if called in the atomic check
23162306a36Sopenharmony_ci * path, and the "new_state" after the atomic swap has been done.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistruct omap_global_state *
23462306a36Sopenharmony_ciomap_get_existing_global_state(struct omap_drm_private *priv)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return to_omap_global_state(priv->glob_obj.state);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * This acquires the modeset lock set aside for global state, creates
24162306a36Sopenharmony_ci * a new duplicated private object state.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistruct omap_global_state *__must_check
24462306a36Sopenharmony_ciomap_get_global_state(struct drm_atomic_state *s)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct omap_drm_private *priv = s->dev->dev_private;
24762306a36Sopenharmony_ci	struct drm_private_state *priv_state;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	priv_state = drm_atomic_get_private_obj_state(s, &priv->glob_obj);
25062306a36Sopenharmony_ci	if (IS_ERR(priv_state))
25162306a36Sopenharmony_ci		return ERR_CAST(priv_state);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return to_omap_global_state(priv_state);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic struct drm_private_state *
25762306a36Sopenharmony_ciomap_global_duplicate_state(struct drm_private_obj *obj)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct omap_global_state *state;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
26262306a36Sopenharmony_ci	if (!state)
26362306a36Sopenharmony_ci		return NULL;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return &state->base;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void omap_global_destroy_state(struct drm_private_obj *obj,
27162306a36Sopenharmony_ci				      struct drm_private_state *state)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct omap_global_state *omap_state = to_omap_global_state(state);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	kfree(omap_state);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic const struct drm_private_state_funcs omap_global_state_funcs = {
27962306a36Sopenharmony_ci	.atomic_duplicate_state = omap_global_duplicate_state,
28062306a36Sopenharmony_ci	.atomic_destroy_state = omap_global_destroy_state,
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int omap_global_obj_init(struct drm_device *dev)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct omap_drm_private *priv = dev->dev_private;
28662306a36Sopenharmony_ci	struct omap_global_state *state;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
28962306a36Sopenharmony_ci	if (!state)
29062306a36Sopenharmony_ci		return -ENOMEM;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	drm_atomic_private_obj_init(dev, &priv->glob_obj, &state->base,
29362306a36Sopenharmony_ci				    &omap_global_state_funcs);
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void omap_global_obj_fini(struct omap_drm_private *priv)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	drm_atomic_private_obj_fini(&priv->glob_obj);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic void omap_disconnect_pipelines(struct drm_device *ddev)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct omap_drm_private *priv = ddev->dev_private;
30562306a36Sopenharmony_ci	unsigned int i;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	for (i = 0; i < priv->num_pipes; i++) {
30862306a36Sopenharmony_ci		struct omap_drm_pipeline *pipe = &priv->pipes[i];
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		omapdss_device_disconnect(NULL, pipe->output);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		omapdss_device_put(pipe->output);
31362306a36Sopenharmony_ci		pipe->output = NULL;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	memset(&priv->channels, 0, sizeof(priv->channels));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	priv->num_pipes = 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int omap_connect_pipelines(struct drm_device *ddev)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct omap_drm_private *priv = ddev->dev_private;
32462306a36Sopenharmony_ci	struct omap_dss_device *output = NULL;
32562306a36Sopenharmony_ci	int r;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	for_each_dss_output(output) {
32862306a36Sopenharmony_ci		r = omapdss_device_connect(priv->dss, NULL, output);
32962306a36Sopenharmony_ci		if (r == -EPROBE_DEFER) {
33062306a36Sopenharmony_ci			omapdss_device_put(output);
33162306a36Sopenharmony_ci			return r;
33262306a36Sopenharmony_ci		} else if (r) {
33362306a36Sopenharmony_ci			dev_warn(output->dev, "could not connect output %s\n",
33462306a36Sopenharmony_ci				 output->name);
33562306a36Sopenharmony_ci		} else {
33662306a36Sopenharmony_ci			struct omap_drm_pipeline *pipe;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci			pipe = &priv->pipes[priv->num_pipes++];
33962306a36Sopenharmony_ci			pipe->output = omapdss_device_get(output);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			if (priv->num_pipes == ARRAY_SIZE(priv->pipes)) {
34262306a36Sopenharmony_ci				/* To balance the 'for_each_dss_output' loop */
34362306a36Sopenharmony_ci				omapdss_device_put(output);
34462306a36Sopenharmony_ci				break;
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int omap_compare_pipelines(const void *a, const void *b)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	const struct omap_drm_pipeline *pipe1 = a;
35562306a36Sopenharmony_ci	const struct omap_drm_pipeline *pipe2 = b;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (pipe1->alias_id > pipe2->alias_id)
35862306a36Sopenharmony_ci		return 1;
35962306a36Sopenharmony_ci	else if (pipe1->alias_id < pipe2->alias_id)
36062306a36Sopenharmony_ci		return -1;
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int omap_modeset_init_properties(struct drm_device *dev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct omap_drm_private *priv = dev->dev_private;
36762306a36Sopenharmony_ci	unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0,
37062306a36Sopenharmony_ci						      num_planes - 1);
37162306a36Sopenharmony_ci	if (!priv->zorder_prop)
37262306a36Sopenharmony_ci		return -ENOMEM;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int omap_display_id(struct omap_dss_device *output)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct device_node *node = NULL;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (output->bridge) {
38262306a36Sopenharmony_ci		struct drm_bridge *bridge = output->bridge;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		while (drm_bridge_get_next_bridge(bridge))
38562306a36Sopenharmony_ci			bridge = drm_bridge_get_next_bridge(bridge);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		node = bridge->of_node;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return node ? of_alias_get_id(node, "display") : -ENODEV;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int omap_modeset_init(struct drm_device *dev)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct omap_drm_private *priv = dev->dev_private;
39662306a36Sopenharmony_ci	int num_ovls = dispc_get_num_ovls(priv->dispc);
39762306a36Sopenharmony_ci	int num_mgrs = dispc_get_num_mgrs(priv->dispc);
39862306a36Sopenharmony_ci	unsigned int i;
39962306a36Sopenharmony_ci	int ret;
40062306a36Sopenharmony_ci	u32 plane_crtc_mask;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (!omapdss_stack_is_ready())
40362306a36Sopenharmony_ci		return -EPROBE_DEFER;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = omap_modeset_init_properties(dev);
40662306a36Sopenharmony_ci	if (ret < 0)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*
41062306a36Sopenharmony_ci	 * This function creates exactly one connector, encoder, crtc,
41162306a36Sopenharmony_ci	 * and primary plane per each connected dss-device. Each
41262306a36Sopenharmony_ci	 * connector->encoder->crtc chain is expected to be separate
41362306a36Sopenharmony_ci	 * and each crtc is connect to a single dss-channel. If the
41462306a36Sopenharmony_ci	 * configuration does not match the expectations or exceeds
41562306a36Sopenharmony_ci	 * the available resources, the configuration is rejected.
41662306a36Sopenharmony_ci	 */
41762306a36Sopenharmony_ci	ret = omap_connect_pipelines(dev);
41862306a36Sopenharmony_ci	if (ret < 0)
41962306a36Sopenharmony_ci		return ret;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (priv->num_pipes > num_mgrs || priv->num_pipes > num_ovls) {
42262306a36Sopenharmony_ci		dev_err(dev->dev, "%s(): Too many connected displays\n",
42362306a36Sopenharmony_ci			__func__);
42462306a36Sopenharmony_ci		return -EINVAL;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Create all planes first. They can all be put to any CRTC. */
42862306a36Sopenharmony_ci	plane_crtc_mask = (1 << priv->num_pipes) - 1;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	for (i = 0; i < num_ovls; i++) {
43162306a36Sopenharmony_ci		enum drm_plane_type type = i < priv->num_pipes
43262306a36Sopenharmony_ci					 ? DRM_PLANE_TYPE_PRIMARY
43362306a36Sopenharmony_ci					 : DRM_PLANE_TYPE_OVERLAY;
43462306a36Sopenharmony_ci		struct drm_plane *plane;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		if (WARN_ON(priv->num_planes >= ARRAY_SIZE(priv->planes)))
43762306a36Sopenharmony_ci			return -EINVAL;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		plane = omap_plane_init(dev, i, type, plane_crtc_mask);
44062306a36Sopenharmony_ci		if (IS_ERR(plane))
44162306a36Sopenharmony_ci			return PTR_ERR(plane);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		priv->planes[priv->num_planes++] = plane;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/*
44762306a36Sopenharmony_ci	 * Create the encoders, attach the bridges and get the pipeline alias
44862306a36Sopenharmony_ci	 * IDs.
44962306a36Sopenharmony_ci	 */
45062306a36Sopenharmony_ci	for (i = 0; i < priv->num_pipes; i++) {
45162306a36Sopenharmony_ci		struct omap_drm_pipeline *pipe = &priv->pipes[i];
45262306a36Sopenharmony_ci		int id;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		pipe->encoder = omap_encoder_init(dev, pipe->output);
45562306a36Sopenharmony_ci		if (!pipe->encoder)
45662306a36Sopenharmony_ci			return -ENOMEM;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (pipe->output->bridge) {
45962306a36Sopenharmony_ci			ret = drm_bridge_attach(pipe->encoder,
46062306a36Sopenharmony_ci						pipe->output->bridge, NULL,
46162306a36Sopenharmony_ci						DRM_BRIDGE_ATTACH_NO_CONNECTOR);
46262306a36Sopenharmony_ci			if (ret < 0)
46362306a36Sopenharmony_ci				return ret;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		id = omap_display_id(pipe->output);
46762306a36Sopenharmony_ci		pipe->alias_id = id >= 0 ? id : i;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Sort the pipelines by DT aliases. */
47162306a36Sopenharmony_ci	sort(priv->pipes, priv->num_pipes, sizeof(priv->pipes[0]),
47262306a36Sopenharmony_ci	     omap_compare_pipelines, NULL);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * Populate the pipeline lookup table by DISPC channel. Only one display
47662306a36Sopenharmony_ci	 * is allowed per channel.
47762306a36Sopenharmony_ci	 */
47862306a36Sopenharmony_ci	for (i = 0; i < priv->num_pipes; ++i) {
47962306a36Sopenharmony_ci		struct omap_drm_pipeline *pipe = &priv->pipes[i];
48062306a36Sopenharmony_ci		enum omap_channel channel = pipe->output->dispc_channel;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		if (WARN_ON(priv->channels[channel] != NULL))
48362306a36Sopenharmony_ci			return -EINVAL;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		priv->channels[channel] = pipe;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Create the connectors and CRTCs. */
48962306a36Sopenharmony_ci	for (i = 0; i < priv->num_pipes; i++) {
49062306a36Sopenharmony_ci		struct omap_drm_pipeline *pipe = &priv->pipes[i];
49162306a36Sopenharmony_ci		struct drm_encoder *encoder = pipe->encoder;
49262306a36Sopenharmony_ci		struct drm_crtc *crtc;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		pipe->connector = drm_bridge_connector_init(dev, encoder);
49562306a36Sopenharmony_ci		if (IS_ERR(pipe->connector)) {
49662306a36Sopenharmony_ci			dev_err(priv->dev,
49762306a36Sopenharmony_ci				"unable to create bridge connector for %s\n",
49862306a36Sopenharmony_ci				pipe->output->name);
49962306a36Sopenharmony_ci			return PTR_ERR(pipe->connector);
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		drm_connector_attach_encoder(pipe->connector, encoder);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
50562306a36Sopenharmony_ci		if (IS_ERR(crtc))
50662306a36Sopenharmony_ci			return PTR_ERR(crtc);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		encoder->possible_crtcs = 1 << i;
50962306a36Sopenharmony_ci		pipe->crtc = crtc;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	DBG("registered %u planes, %u crtcs/encoders/connectors\n",
51362306a36Sopenharmony_ci	    priv->num_planes, priv->num_pipes);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	dev->mode_config.min_width = 8;
51662306a36Sopenharmony_ci	dev->mode_config.min_height = 2;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/*
51962306a36Sopenharmony_ci	 * Note: these values are used for multiple independent things:
52062306a36Sopenharmony_ci	 * connector mode filtering, buffer sizes, crtc sizes...
52162306a36Sopenharmony_ci	 * Use big enough values here to cover all use cases, and do more
52262306a36Sopenharmony_ci	 * specific checking in the respective code paths.
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	dev->mode_config.max_width = 8192;
52562306a36Sopenharmony_ci	dev->mode_config.max_height = 8192;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* We want the zpos to be normalized */
52862306a36Sopenharmony_ci	dev->mode_config.normalize_zpos = true;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	dev->mode_config.funcs = &omap_mode_config_funcs;
53162306a36Sopenharmony_ci	dev->mode_config.helper_private = &omap_mode_config_helper_funcs;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	drm_mode_config_reset(dev);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	omap_drm_irq_install(dev);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	return 0;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void omap_modeset_fini(struct drm_device *ddev)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	omap_drm_irq_uninstall(ddev);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	drm_mode_config_cleanup(ddev);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*
54862306a36Sopenharmony_ci * drm ioctl funcs
54962306a36Sopenharmony_ci */
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic int ioctl_get_param(struct drm_device *dev, void *data,
55362306a36Sopenharmony_ci		struct drm_file *file_priv)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct omap_drm_private *priv = dev->dev_private;
55662306a36Sopenharmony_ci	struct drm_omap_param *args = data;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	DBG("%p: param=%llu", dev, args->param);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	switch (args->param) {
56162306a36Sopenharmony_ci	case OMAP_PARAM_CHIPSET_ID:
56262306a36Sopenharmony_ci		args->value = priv->omaprev;
56362306a36Sopenharmony_ci		break;
56462306a36Sopenharmony_ci	default:
56562306a36Sopenharmony_ci		DBG("unknown parameter %lld", args->param);
56662306a36Sopenharmony_ci		return -EINVAL;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci#define OMAP_BO_USER_MASK	0x00ffffff	/* flags settable by userspace */
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic int ioctl_gem_new(struct drm_device *dev, void *data,
57562306a36Sopenharmony_ci		struct drm_file *file_priv)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct drm_omap_gem_new *args = data;
57862306a36Sopenharmony_ci	u32 flags = args->flags & OMAP_BO_USER_MASK;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
58162306a36Sopenharmony_ci	     args->size.bytes, flags);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return omap_gem_new_handle(dev, file_priv, args->size, flags,
58462306a36Sopenharmony_ci				   &args->handle);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int ioctl_gem_info(struct drm_device *dev, void *data,
58862306a36Sopenharmony_ci		struct drm_file *file_priv)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct drm_omap_gem_info *args = data;
59162306a36Sopenharmony_ci	struct drm_gem_object *obj;
59262306a36Sopenharmony_ci	int ret = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file_priv, args->handle);
59762306a36Sopenharmony_ci	if (!obj)
59862306a36Sopenharmony_ci		return -ENOENT;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	args->size = omap_gem_mmap_size(obj);
60162306a36Sopenharmony_ci	args->offset = omap_gem_mmap_offset(obj);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	drm_gem_object_put(obj);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return ret;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
60962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param,
61062306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
61162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, drm_invalid_op,
61262306a36Sopenharmony_ci			  DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
61362306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new,
61462306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
61562306a36Sopenharmony_ci	/* Deprecated, to be removed. */
61662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, drm_noop,
61762306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
61862306a36Sopenharmony_ci	/* Deprecated, to be removed. */
61962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, drm_noop,
62062306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
62162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info,
62262306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
62362306a36Sopenharmony_ci};
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/*
62662306a36Sopenharmony_ci * drm driver funcs
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int dev_open(struct drm_device *dev, struct drm_file *file)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	file->driver_priv = NULL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	DBG("open: dev=%p, file=%p", dev, file);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ciDEFINE_DRM_GEM_FOPS(omapdriver_fops);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic const struct drm_driver omap_drm_driver = {
64162306a36Sopenharmony_ci	.driver_features = DRIVER_MODESET | DRIVER_GEM  |
64262306a36Sopenharmony_ci		DRIVER_ATOMIC | DRIVER_RENDER,
64362306a36Sopenharmony_ci	.open = dev_open,
64462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
64562306a36Sopenharmony_ci	.debugfs_init = omap_debugfs_init,
64662306a36Sopenharmony_ci#endif
64762306a36Sopenharmony_ci	.gem_prime_import = omap_gem_prime_import,
64862306a36Sopenharmony_ci	.dumb_create = omap_gem_dumb_create,
64962306a36Sopenharmony_ci	.dumb_map_offset = omap_gem_dumb_map_offset,
65062306a36Sopenharmony_ci	.ioctls = ioctls,
65162306a36Sopenharmony_ci	.num_ioctls = DRM_OMAP_NUM_IOCTLS,
65262306a36Sopenharmony_ci	.fops = &omapdriver_fops,
65362306a36Sopenharmony_ci	.name = DRIVER_NAME,
65462306a36Sopenharmony_ci	.desc = DRIVER_DESC,
65562306a36Sopenharmony_ci	.date = DRIVER_DATE,
65662306a36Sopenharmony_ci	.major = DRIVER_MAJOR,
65762306a36Sopenharmony_ci	.minor = DRIVER_MINOR,
65862306a36Sopenharmony_ci	.patchlevel = DRIVER_PATCHLEVEL,
65962306a36Sopenharmony_ci};
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic const struct soc_device_attribute omapdrm_soc_devices[] = {
66262306a36Sopenharmony_ci	{ .family = "OMAP3", .data = (void *)0x3430 },
66362306a36Sopenharmony_ci	{ .family = "OMAP4", .data = (void *)0x4430 },
66462306a36Sopenharmony_ci	{ .family = "OMAP5", .data = (void *)0x5430 },
66562306a36Sopenharmony_ci	{ .family = "DRA7",  .data = (void *)0x0752 },
66662306a36Sopenharmony_ci	{ /* sentinel */ }
66762306a36Sopenharmony_ci};
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	const struct soc_device_attribute *soc;
67262306a36Sopenharmony_ci	struct dss_pdata *pdata = dev->platform_data;
67362306a36Sopenharmony_ci	struct drm_device *ddev;
67462306a36Sopenharmony_ci	int ret;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	DBG("%s", dev_name(dev));
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (drm_firmware_drivers_only())
67962306a36Sopenharmony_ci		return -ENODEV;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Allocate and initialize the DRM device. */
68262306a36Sopenharmony_ci	ddev = drm_dev_alloc(&omap_drm_driver, dev);
68362306a36Sopenharmony_ci	if (IS_ERR(ddev))
68462306a36Sopenharmony_ci		return PTR_ERR(ddev);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	priv->ddev = ddev;
68762306a36Sopenharmony_ci	ddev->dev_private = priv;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	priv->dev = dev;
69062306a36Sopenharmony_ci	priv->dss = pdata->dss;
69162306a36Sopenharmony_ci	priv->dispc = dispc_get_dispc(priv->dss);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	priv->dss->mgr_ops_priv = priv;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	soc = soc_device_match(omapdrm_soc_devices);
69662306a36Sopenharmony_ci	priv->omaprev = soc ? (uintptr_t)soc->data : 0;
69762306a36Sopenharmony_ci	priv->wq = alloc_ordered_workqueue("omapdrm", 0);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	mutex_init(&priv->list_lock);
70062306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->obj_list);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* Get memory bandwidth limits */
70362306a36Sopenharmony_ci	priv->max_bandwidth = dispc_get_memory_bandwidth_limit(priv->dispc);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	omap_gem_init(ddev);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	drm_mode_config_init(ddev);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	ret = omap_global_obj_init(ddev);
71062306a36Sopenharmony_ci	if (ret)
71162306a36Sopenharmony_ci		goto err_gem_deinit;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	ret = omap_hwoverlays_init(priv);
71462306a36Sopenharmony_ci	if (ret)
71562306a36Sopenharmony_ci		goto err_free_priv_obj;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	ret = omap_modeset_init(ddev);
71862306a36Sopenharmony_ci	if (ret) {
71962306a36Sopenharmony_ci		dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret);
72062306a36Sopenharmony_ci		goto err_free_overlays;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Initialize vblank handling, start with all CRTCs disabled. */
72462306a36Sopenharmony_ci	ret = drm_vblank_init(ddev, priv->num_pipes);
72562306a36Sopenharmony_ci	if (ret) {
72662306a36Sopenharmony_ci		dev_err(priv->dev, "could not init vblank\n");
72762306a36Sopenharmony_ci		goto err_cleanup_modeset;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	drm_kms_helper_poll_init(ddev);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * Register the DRM device with the core and the connectors with
73462306a36Sopenharmony_ci	 * sysfs.
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	ret = drm_dev_register(ddev, 0);
73762306a36Sopenharmony_ci	if (ret)
73862306a36Sopenharmony_ci		goto err_cleanup_helpers;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	omap_fbdev_setup(ddev);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cierr_cleanup_helpers:
74562306a36Sopenharmony_ci	drm_kms_helper_poll_fini(ddev);
74662306a36Sopenharmony_cierr_cleanup_modeset:
74762306a36Sopenharmony_ci	omap_modeset_fini(ddev);
74862306a36Sopenharmony_cierr_free_overlays:
74962306a36Sopenharmony_ci	omap_hwoverlays_destroy(priv);
75062306a36Sopenharmony_cierr_free_priv_obj:
75162306a36Sopenharmony_ci	omap_global_obj_fini(priv);
75262306a36Sopenharmony_cierr_gem_deinit:
75362306a36Sopenharmony_ci	drm_mode_config_cleanup(ddev);
75462306a36Sopenharmony_ci	omap_gem_deinit(ddev);
75562306a36Sopenharmony_ci	destroy_workqueue(priv->wq);
75662306a36Sopenharmony_ci	omap_disconnect_pipelines(ddev);
75762306a36Sopenharmony_ci	drm_dev_put(ddev);
75862306a36Sopenharmony_ci	return ret;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic void omapdrm_cleanup(struct omap_drm_private *priv)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct drm_device *ddev = priv->ddev;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	DBG("");
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	drm_dev_unregister(ddev);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	drm_kms_helper_poll_fini(ddev);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	drm_atomic_helper_shutdown(ddev);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	omap_modeset_fini(ddev);
77462306a36Sopenharmony_ci	omap_hwoverlays_destroy(priv);
77562306a36Sopenharmony_ci	omap_global_obj_fini(priv);
77662306a36Sopenharmony_ci	drm_mode_config_cleanup(ddev);
77762306a36Sopenharmony_ci	omap_gem_deinit(ddev);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	destroy_workqueue(priv->wq);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	omap_disconnect_pipelines(ddev);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	drm_dev_put(ddev);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic int pdev_probe(struct platform_device *pdev)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct omap_drm_private *priv;
78962306a36Sopenharmony_ci	int ret;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
79262306a36Sopenharmony_ci	if (ret) {
79362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to set the DMA mask\n");
79462306a36Sopenharmony_ci		return ret;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* Allocate and initialize the driver private structure. */
79862306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
79962306a36Sopenharmony_ci	if (!priv)
80062306a36Sopenharmony_ci		return -ENOMEM;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	ret = omapdrm_init(priv, &pdev->dev);
80562306a36Sopenharmony_ci	if (ret < 0)
80662306a36Sopenharmony_ci		kfree(priv);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return ret;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic void pdev_remove(struct platform_device *pdev)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct omap_drm_private *priv = platform_get_drvdata(pdev);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	omapdrm_cleanup(priv);
81662306a36Sopenharmony_ci	kfree(priv);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
82062306a36Sopenharmony_cistatic int omap_drm_suspend(struct device *dev)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct omap_drm_private *priv = dev_get_drvdata(dev);
82362306a36Sopenharmony_ci	struct drm_device *drm_dev = priv->ddev;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return drm_mode_config_helper_suspend(drm_dev);
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int omap_drm_resume(struct device *dev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct omap_drm_private *priv = dev_get_drvdata(dev);
83162306a36Sopenharmony_ci	struct drm_device *drm_dev = priv->ddev;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	drm_mode_config_helper_resume(drm_dev);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	return omap_gem_resume(drm_dev);
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci#endif
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic struct platform_driver pdev = {
84262306a36Sopenharmony_ci	.driver = {
84362306a36Sopenharmony_ci		.name = "omapdrm",
84462306a36Sopenharmony_ci		.pm = &omapdrm_pm_ops,
84562306a36Sopenharmony_ci	},
84662306a36Sopenharmony_ci	.probe = pdev_probe,
84762306a36Sopenharmony_ci	.remove_new = pdev_remove,
84862306a36Sopenharmony_ci};
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic struct platform_driver * const drivers[] = {
85162306a36Sopenharmony_ci	&omap_dmm_driver,
85262306a36Sopenharmony_ci	&pdev,
85362306a36Sopenharmony_ci};
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int __init omap_drm_init(void)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	int r;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	DBG("init");
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	r = omap_dss_init();
86262306a36Sopenharmony_ci	if (r)
86362306a36Sopenharmony_ci		return r;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	r = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
86662306a36Sopenharmony_ci	if (r) {
86762306a36Sopenharmony_ci		omap_dss_exit();
86862306a36Sopenharmony_ci		return r;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic void __exit omap_drm_fini(void)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	DBG("fini");
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	omap_dss_exit();
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cimodule_init(omap_drm_init);
88462306a36Sopenharmony_cimodule_exit(omap_drm_fini);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ciMODULE_AUTHOR("Rob Clark <rob@ti.com>");
88762306a36Sopenharmony_ciMODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
88862306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP DRM Display Driver");
88962306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
89062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
891