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