18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. 48c2ecf20Sopenharmony_ci * Author: Liviu Dudau <Liviu.Dudau@arm.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * ARM Mali DP500/DP550/DP650 KMS/DRM driver 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/component.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 148c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h> 158c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 168c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "malidp_drv.h" 338c2ecf20Sopenharmony_ci#include "malidp_mw.h" 348c2ecf20Sopenharmony_ci#include "malidp_regs.h" 358c2ecf20Sopenharmony_ci#include "malidp_hw.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MALIDP_CONF_VALID_TIMEOUT 250 388c2ecf20Sopenharmony_ci#define AFBC_HEADER_SIZE 16 398c2ecf20Sopenharmony_ci#define AFBC_SUPERBLK_ALIGNMENT 128 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void malidp_write_gamma_table(struct malidp_hw_device *hwdev, 428c2ecf20Sopenharmony_ci u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int i; 458c2ecf20Sopenharmony_ci /* Update all channels with a single gamma curve. */ 468c2ecf20Sopenharmony_ci const u32 gamma_write_mask = GENMASK(18, 16); 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Always write an entire table, so the address field in 498c2ecf20Sopenharmony_ci * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask 508c2ecf20Sopenharmony_ci * directly. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, gamma_write_mask, 538c2ecf20Sopenharmony_ci hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); 548c2ecf20Sopenharmony_ci for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) 558c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, data[i], 568c2ecf20Sopenharmony_ci hwdev->hw->map.coeffs_base + 578c2ecf20Sopenharmony_ci MALIDP_COEF_TABLE_DATA); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc, 618c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 648c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (!crtc->state->color_mgmt_changed) 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!crtc->state->gamma_lut) { 708c2ecf20Sopenharmony_ci malidp_hw_clearbits(hwdev, 718c2ecf20Sopenharmony_ci MALIDP_DISP_FUNC_GAMMA, 728c2ecf20Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 738c2ecf20Sopenharmony_ci } else { 748c2ecf20Sopenharmony_ci struct malidp_crtc_state *mc = 758c2ecf20Sopenharmony_ci to_malidp_crtc_state(crtc->state); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id != 788c2ecf20Sopenharmony_ci old_state->gamma_lut->base.id)) 798c2ecf20Sopenharmony_ci malidp_write_gamma_table(hwdev, mc->gamma_coeffs); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA, 828c2ecf20Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic 878c2ecf20Sopenharmony_civoid malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, 888c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 918c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 928c2ecf20Sopenharmony_ci int i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (!crtc->state->color_mgmt_changed) 958c2ecf20Sopenharmony_ci return; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!crtc->state->ctm) { 988c2ecf20Sopenharmony_ci malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ, 998c2ecf20Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 1008c2ecf20Sopenharmony_ci } else { 1018c2ecf20Sopenharmony_ci struct malidp_crtc_state *mc = 1028c2ecf20Sopenharmony_ci to_malidp_crtc_state(crtc->state); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!old_state->ctm || (crtc->state->ctm->base.id != 1058c2ecf20Sopenharmony_ci old_state->ctm->base.id)) 1068c2ecf20Sopenharmony_ci for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) 1078c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, 1088c2ecf20Sopenharmony_ci mc->coloradj_coeffs[i], 1098c2ecf20Sopenharmony_ci hwdev->hw->map.coeffs_base + 1108c2ecf20Sopenharmony_ci MALIDP_COLOR_ADJ_COEF + 4 * i); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, 1138c2ecf20Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void malidp_atomic_commit_se_config(struct drm_crtc *crtc, 1188c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state); 1218c2ecf20Sopenharmony_ci struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state); 1228c2ecf20Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 1238c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 1248c2ecf20Sopenharmony_ci struct malidp_se_config *s = &cs->scaler_config; 1258c2ecf20Sopenharmony_ci struct malidp_se_config *old_s = &old_cs->scaler_config; 1268c2ecf20Sopenharmony_ci u32 se_control = hwdev->hw->map.se_base + 1278c2ecf20Sopenharmony_ci ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? 1288c2ecf20Sopenharmony_ci 0x10 : 0xC); 1298c2ecf20Sopenharmony_ci u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; 1308c2ecf20Sopenharmony_ci u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; 1318c2ecf20Sopenharmony_ci u32 val; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Set SE_CONTROL */ 1348c2ecf20Sopenharmony_ci if (!s->scale_enable) { 1358c2ecf20Sopenharmony_ci val = malidp_hw_read(hwdev, se_control); 1368c2ecf20Sopenharmony_ci val &= ~MALIDP_SE_SCALING_EN; 1378c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, val, se_control); 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); 1428c2ecf20Sopenharmony_ci val = malidp_hw_read(hwdev, se_control); 1438c2ecf20Sopenharmony_ci val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK); 1468c2ecf20Sopenharmony_ci val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci val |= MALIDP_SE_RGBO_IF_EN; 1498c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, val, se_control); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Set IN_SIZE & OUT_SIZE. */ 1528c2ecf20Sopenharmony_ci val = MALIDP_SE_SET_V_SIZE(s->input_h) | 1538c2ecf20Sopenharmony_ci MALIDP_SE_SET_H_SIZE(s->input_w); 1548c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE); 1558c2ecf20Sopenharmony_ci val = MALIDP_SE_SET_V_SIZE(s->output_h) | 1568c2ecf20Sopenharmony_ci MALIDP_SE_SET_H_SIZE(s->output_w); 1578c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Set phase regs. */ 1608c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH); 1618c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH); 1628c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH); 1638c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * set the "config valid" bit and wait until the hardware acts on it 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistatic int malidp_set_and_wait_config_valid(struct drm_device *drm) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 1728c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci hwdev->hw->set_config_valid(hwdev, 1); 1768c2ecf20Sopenharmony_ci /* don't wait for config_valid flag if we are in config mode */ 1778c2ecf20Sopenharmony_ci if (hwdev->hw->in_config_mode(hwdev)) { 1788c2ecf20Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(malidp->wq, 1838c2ecf20Sopenharmony_ci atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, 1848c2ecf20Sopenharmony_ci msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return (ret > 0) ? 0 : -ETIMEDOUT; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct drm_device *drm = state->dev; 1928c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 1938c2ecf20Sopenharmony_ci int loop = 5; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci malidp->event = malidp->crtc.state->event; 1968c2ecf20Sopenharmony_ci malidp->crtc.state->event = NULL; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (malidp->crtc.state->active) { 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * if we have an event to deliver to userspace, make sure 2018c2ecf20Sopenharmony_ci * the vblank is enabled as we are sending it from the IRQ 2028c2ecf20Sopenharmony_ci * handler. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci if (malidp->event) 2058c2ecf20Sopenharmony_ci drm_crtc_vblank_get(&malidp->crtc); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* only set config_valid if the CRTC is enabled */ 2088c2ecf20Sopenharmony_ci if (malidp_set_and_wait_config_valid(drm) < 0) { 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * make a loop around the second CVAL setting and 2118c2ecf20Sopenharmony_ci * try 5 times before giving up. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci while (loop--) { 2148c2ecf20Sopenharmony_ci if (!malidp_set_and_wait_config_valid(drm)) 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci } else if (malidp->event) { 2218c2ecf20Sopenharmony_ci /* CRTC inactive means vblank IRQ is disabled, send event directly */ 2228c2ecf20Sopenharmony_ci spin_lock_irq(&drm->event_lock); 2238c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(&malidp->crtc, malidp->event); 2248c2ecf20Sopenharmony_ci malidp->event = NULL; 2258c2ecf20Sopenharmony_ci spin_unlock_irq(&drm->event_lock); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci drm_atomic_helper_commit_hw_done(state); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void malidp_atomic_commit_tail(struct drm_atomic_state *state) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct drm_device *drm = state->dev; 2338c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 2348c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 2358c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state; 2368c2ecf20Sopenharmony_ci int i; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci pm_runtime_get_sync(drm->dev); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * set config_valid to a special value to let IRQ handlers 2428c2ecf20Sopenharmony_ci * know that we are updating registers 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); 2458c2ecf20Sopenharmony_ci malidp->dev->hw->set_config_valid(malidp->dev, 0); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci drm_atomic_helper_commit_modeset_disables(drm, state); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { 2508c2ecf20Sopenharmony_ci malidp_atomic_commit_update_gamma(crtc, old_crtc_state); 2518c2ecf20Sopenharmony_ci malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); 2528c2ecf20Sopenharmony_ci malidp_atomic_commit_se_config(crtc, old_crtc_state); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci malidp_mw_atomic_commit(drm, state); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci drm_atomic_helper_commit_modeset_enables(drm, state); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci malidp_atomic_commit_hw_done(state); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci pm_runtime_put(drm->dev); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci drm_atomic_helper_cleanup_planes(drm, state); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { 2698c2ecf20Sopenharmony_ci .atomic_commit_tail = malidp_atomic_commit_tail, 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic bool 2738c2ecf20Sopenharmony_cimalidp_verify_afbc_framebuffer_caps(struct drm_device *dev, 2748c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (malidp_format_mod_supported(dev, mode_cmd->pixel_format, 2778c2ecf20Sopenharmony_ci mode_cmd->modifier[0]) == false) 2788c2ecf20Sopenharmony_ci return false; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (mode_cmd->offsets[0] != 0) { 2818c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); 2828c2ecf20Sopenharmony_ci return false; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { 2868c2ecf20Sopenharmony_ci case AFBC_SIZE_16X16: 2878c2ecf20Sopenharmony_ci if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { 2888c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); 2898c2ecf20Sopenharmony_ci return false; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unsupported AFBC block size\n"); 2948c2ecf20Sopenharmony_ci return false; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return true; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic bool 3018c2ecf20Sopenharmony_cimalidp_verify_afbc_framebuffer_size(struct drm_device *dev, 3028c2ecf20Sopenharmony_ci struct drm_file *file, 3038c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int n_superblocks = 0; 3068c2ecf20Sopenharmony_ci const struct drm_format_info *info; 3078c2ecf20Sopenharmony_ci struct drm_gem_object *objs = NULL; 3088c2ecf20Sopenharmony_ci u32 afbc_superblock_size = 0, afbc_superblock_height = 0; 3098c2ecf20Sopenharmony_ci u32 afbc_superblock_width = 0, afbc_size = 0; 3108c2ecf20Sopenharmony_ci int bpp = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { 3138c2ecf20Sopenharmony_ci case AFBC_SIZE_16X16: 3148c2ecf20Sopenharmony_ci afbc_superblock_height = 16; 3158c2ecf20Sopenharmony_ci afbc_superblock_width = 16; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("AFBC superblock size is not supported\n"); 3198c2ecf20Sopenharmony_ci return false; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci info = drm_get_format_info(dev, mode_cmd); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci n_superblocks = (mode_cmd->width / afbc_superblock_width) * 3258c2ecf20Sopenharmony_ci (mode_cmd->height / afbc_superblock_height); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci bpp = malidp_format_get_bpp(info->format); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height) 3308c2ecf20Sopenharmony_ci / BITS_PER_BYTE; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); 3338c2ecf20Sopenharmony_ci afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) { 3368c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) " 3378c2ecf20Sopenharmony_ci "should be same as width (=%u) * bpp (=%u)\n", 3388c2ecf20Sopenharmony_ci (mode_cmd->pitches[0] * BITS_PER_BYTE), 3398c2ecf20Sopenharmony_ci mode_cmd->width, bpp); 3408c2ecf20Sopenharmony_ci return false; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci objs = drm_gem_object_lookup(file, mode_cmd->handles[0]); 3448c2ecf20Sopenharmony_ci if (!objs) { 3458c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 3468c2ecf20Sopenharmony_ci return false; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (objs->size < afbc_size) { 3508c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("buffer size (%zu) too small for AFBC buffer size = %u\n", 3518c2ecf20Sopenharmony_ci objs->size, afbc_size); 3528c2ecf20Sopenharmony_ci drm_gem_object_put(objs); 3538c2ecf20Sopenharmony_ci return false; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci drm_gem_object_put(objs); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return true; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic bool 3628c2ecf20Sopenharmony_cimalidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file, 3638c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci if (malidp_verify_afbc_framebuffer_caps(dev, mode_cmd)) 3668c2ecf20Sopenharmony_ci return malidp_verify_afbc_framebuffer_size(dev, file, mode_cmd); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return false; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic struct drm_framebuffer * 3728c2ecf20Sopenharmony_cimalidp_fb_create(struct drm_device *dev, struct drm_file *file, 3738c2ecf20Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci if (mode_cmd->modifier[0]) { 3768c2ecf20Sopenharmony_ci if (!malidp_verify_afbc_framebuffer(dev, file, mode_cmd)) 3778c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return drm_gem_fb_create(dev, file, mode_cmd); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs malidp_mode_config_funcs = { 3848c2ecf20Sopenharmony_ci .fb_create = malidp_fb_create, 3858c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 3868c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int malidp_init(struct drm_device *drm) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int ret; 3928c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 3938c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci drm_mode_config_init(drm); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci drm->mode_config.min_width = hwdev->min_line_size; 3988c2ecf20Sopenharmony_ci drm->mode_config.min_height = hwdev->min_line_size; 3998c2ecf20Sopenharmony_ci drm->mode_config.max_width = hwdev->max_line_size; 4008c2ecf20Sopenharmony_ci drm->mode_config.max_height = hwdev->max_line_size; 4018c2ecf20Sopenharmony_ci drm->mode_config.funcs = &malidp_mode_config_funcs; 4028c2ecf20Sopenharmony_ci drm->mode_config.helper_private = &malidp_mode_config_helpers; 4038c2ecf20Sopenharmony_ci drm->mode_config.allow_fb_modifiers = true; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ret = malidp_crtc_init(drm); 4068c2ecf20Sopenharmony_ci if (ret) 4078c2ecf20Sopenharmony_ci goto crtc_fail; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ret = malidp_mw_connector_init(drm); 4108c2ecf20Sopenharmony_ci if (ret) 4118c2ecf20Sopenharmony_ci goto crtc_fail; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cicrtc_fail: 4168c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void malidp_fini(struct drm_device *drm) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int malidp_irq_init(struct platform_device *pdev) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int irq_de, irq_se, ret = 0; 4288c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(&pdev->dev); 4298c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 4308c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* fetch the interrupts from DT */ 4338c2ecf20Sopenharmony_ci irq_de = platform_get_irq_byname(pdev, "DE"); 4348c2ecf20Sopenharmony_ci if (irq_de < 0) { 4358c2ecf20Sopenharmony_ci DRM_ERROR("no 'DE' IRQ specified!\n"); 4368c2ecf20Sopenharmony_ci return irq_de; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci irq_se = platform_get_irq_byname(pdev, "SE"); 4398c2ecf20Sopenharmony_ci if (irq_se < 0) { 4408c2ecf20Sopenharmony_ci DRM_ERROR("no 'SE' IRQ specified!\n"); 4418c2ecf20Sopenharmony_ci return irq_se; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = malidp_de_irq_init(drm, irq_de); 4458c2ecf20Sopenharmony_ci if (ret) 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ret = malidp_se_irq_init(drm, irq_se); 4498c2ecf20Sopenharmony_ci if (ret) { 4508c2ecf20Sopenharmony_ci malidp_de_irq_fini(hwdev); 4518c2ecf20Sopenharmony_ci return ret; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(fops); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int malidp_dumb_create(struct drm_file *file_priv, 4608c2ecf20Sopenharmony_ci struct drm_device *drm, 4618c2ecf20Sopenharmony_ci struct drm_mode_create_dumb *args) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 4648c2ecf20Sopenharmony_ci /* allocate for the worst case scenario, i.e. rotated buffers */ 4658c2ecf20Sopenharmony_ci u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return drm_gem_cma_dumb_create_internal(file_priv, drm, args); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void malidp_error_stats_init(struct malidp_error_stats *error_stats) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci error_stats->num_errors = 0; 4778c2ecf20Sopenharmony_ci error_stats->last_error_status = 0; 4788c2ecf20Sopenharmony_ci error_stats->last_error_vblank = -1; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid malidp_error(struct malidp_drm *malidp, 4828c2ecf20Sopenharmony_ci struct malidp_error_stats *error_stats, u32 status, 4838c2ecf20Sopenharmony_ci u64 vblank) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci unsigned long irqflags; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 4888c2ecf20Sopenharmony_ci error_stats->last_error_status = status; 4898c2ecf20Sopenharmony_ci error_stats->last_error_vblank = vblank; 4908c2ecf20Sopenharmony_ci error_stats->num_errors++; 4918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void malidp_error_stats_dump(const char *prefix, 4958c2ecf20Sopenharmony_ci struct malidp_error_stats error_stats, 4968c2ecf20Sopenharmony_ci struct seq_file *m) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci seq_printf(m, "[%s] num_errors : %d\n", prefix, 4998c2ecf20Sopenharmony_ci error_stats.num_errors); 5008c2ecf20Sopenharmony_ci seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, 5018c2ecf20Sopenharmony_ci error_stats.last_error_status); 5028c2ecf20Sopenharmony_ci seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, 5038c2ecf20Sopenharmony_ci error_stats.last_error_vblank); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int malidp_show_stats(struct seq_file *m, void *arg) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct drm_device *drm = m->private; 5098c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 5108c2ecf20Sopenharmony_ci unsigned long irqflags; 5118c2ecf20Sopenharmony_ci struct malidp_error_stats de_errors, se_errors; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 5148c2ecf20Sopenharmony_ci de_errors = malidp->de_errors; 5158c2ecf20Sopenharmony_ci se_errors = malidp->se_errors; 5168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 5178c2ecf20Sopenharmony_ci malidp_error_stats_dump("DE", de_errors, m); 5188c2ecf20Sopenharmony_ci malidp_error_stats_dump("SE", se_errors, m); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int malidp_debugfs_open(struct inode *inode, struct file *file) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci return single_open(file, malidp_show_stats, inode->i_private); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, 5288c2ecf20Sopenharmony_ci size_t len, loff_t *offp) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct seq_file *m = file->private_data; 5318c2ecf20Sopenharmony_ci struct drm_device *drm = m->private; 5328c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 5338c2ecf20Sopenharmony_ci unsigned long irqflags; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 5368c2ecf20Sopenharmony_ci malidp_error_stats_init(&malidp->de_errors); 5378c2ecf20Sopenharmony_ci malidp_error_stats_init(&malidp->se_errors); 5388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 5398c2ecf20Sopenharmony_ci return len; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic const struct file_operations malidp_debugfs_fops = { 5438c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5448c2ecf20Sopenharmony_ci .open = malidp_debugfs_open, 5458c2ecf20Sopenharmony_ci .read = seq_read, 5468c2ecf20Sopenharmony_ci .write = malidp_debugfs_write, 5478c2ecf20Sopenharmony_ci .llseek = seq_lseek, 5488c2ecf20Sopenharmony_ci .release = single_release, 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void malidp_debugfs_init(struct drm_minor *minor) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct malidp_drm *malidp = minor->dev->dev_private; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci malidp_error_stats_init(&malidp->de_errors); 5568c2ecf20Sopenharmony_ci malidp_error_stats_init(&malidp->se_errors); 5578c2ecf20Sopenharmony_ci spin_lock_init(&malidp->errors_lock); 5588c2ecf20Sopenharmony_ci debugfs_create_file("debug", S_IRUGO | S_IWUSR, minor->debugfs_root, 5598c2ecf20Sopenharmony_ci minor->dev, &malidp_debugfs_fops); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci#endif //CONFIG_DEBUG_FS 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic struct drm_driver malidp_driver = { 5658c2ecf20Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 5668c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(malidp_dumb_create), 5678c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5688c2ecf20Sopenharmony_ci .debugfs_init = malidp_debugfs_init, 5698c2ecf20Sopenharmony_ci#endif 5708c2ecf20Sopenharmony_ci .fops = &fops, 5718c2ecf20Sopenharmony_ci .name = "mali-dp", 5728c2ecf20Sopenharmony_ci .desc = "ARM Mali Display Processor driver", 5738c2ecf20Sopenharmony_ci .date = "20160106", 5748c2ecf20Sopenharmony_ci .major = 1, 5758c2ecf20Sopenharmony_ci .minor = 0, 5768c2ecf20Sopenharmony_ci}; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic const struct of_device_id malidp_drm_of_match[] = { 5798c2ecf20Sopenharmony_ci { 5808c2ecf20Sopenharmony_ci .compatible = "arm,mali-dp500", 5818c2ecf20Sopenharmony_ci .data = &malidp_device[MALIDP_500] 5828c2ecf20Sopenharmony_ci }, 5838c2ecf20Sopenharmony_ci { 5848c2ecf20Sopenharmony_ci .compatible = "arm,mali-dp550", 5858c2ecf20Sopenharmony_ci .data = &malidp_device[MALIDP_550] 5868c2ecf20Sopenharmony_ci }, 5878c2ecf20Sopenharmony_ci { 5888c2ecf20Sopenharmony_ci .compatible = "arm,mali-dp650", 5898c2ecf20Sopenharmony_ci .data = &malidp_device[MALIDP_650] 5908c2ecf20Sopenharmony_ci }, 5918c2ecf20Sopenharmony_ci {}, 5928c2ecf20Sopenharmony_ci}; 5938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, malidp_drm_of_match); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, 5968c2ecf20Sopenharmony_ci const struct of_device_id *dev_id) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci u32 core_id; 5998c2ecf20Sopenharmony_ci const char *compatstr_dp500 = "arm,mali-dp500"; 6008c2ecf20Sopenharmony_ci bool is_dp500; 6018c2ecf20Sopenharmony_ci bool dt_is_dp500; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* 6048c2ecf20Sopenharmony_ci * The DP500 CORE_ID register is in a different location, so check it 6058c2ecf20Sopenharmony_ci * first. If the product id field matches, then this is DP500, otherwise 6068c2ecf20Sopenharmony_ci * check the DP550/650 CORE_ID register. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); 6098c2ecf20Sopenharmony_ci /* Offset 0x18 will never read 0x500 on products other than DP500. */ 6108c2ecf20Sopenharmony_ci is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); 6118c2ecf20Sopenharmony_ci dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, 6128c2ecf20Sopenharmony_ci sizeof(dev_id->compatible)) != NULL; 6138c2ecf20Sopenharmony_ci if (is_dp500 != dt_is_dp500) { 6148c2ecf20Sopenharmony_ci DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", 6158c2ecf20Sopenharmony_ci dev_id->compatible, is_dp500 ? "is" : "is not"); 6168c2ecf20Sopenharmony_ci return false; 6178c2ecf20Sopenharmony_ci } else if (!dt_is_dp500) { 6188c2ecf20Sopenharmony_ci u16 product_id; 6198c2ecf20Sopenharmony_ci char buf[32]; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci core_id = malidp_hw_read(hwdev, 6228c2ecf20Sopenharmony_ci MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); 6238c2ecf20Sopenharmony_ci product_id = MALIDP_PRODUCT_ID(core_id); 6248c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); 6258c2ecf20Sopenharmony_ci if (!strnstr(dev_id->compatible, buf, 6268c2ecf20Sopenharmony_ci sizeof(dev_id->compatible))) { 6278c2ecf20Sopenharmony_ci DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", 6288c2ecf20Sopenharmony_ci dev_id->compatible, product_id); 6298c2ecf20Sopenharmony_ci return false; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci return true; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic bool malidp_has_sufficient_address_space(const struct resource *res, 6368c2ecf20Sopenharmony_ci const struct of_device_id *dev_id) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci resource_size_t res_size = resource_size(res); 6398c2ecf20Sopenharmony_ci const char *compatstr_dp500 = "arm,mali-dp500"; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (!strnstr(dev_id->compatible, compatstr_dp500, 6428c2ecf20Sopenharmony_ci sizeof(dev_id->compatible))) 6438c2ecf20Sopenharmony_ci return res_size >= MALIDP550_ADDR_SPACE_SIZE; 6448c2ecf20Sopenharmony_ci else if (res_size < MALIDP500_ADDR_SPACE_SIZE) 6458c2ecf20Sopenharmony_ci return false; 6468c2ecf20Sopenharmony_ci return true; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic ssize_t core_id_show(struct device *dev, struct device_attribute *attr, 6508c2ecf20Sopenharmony_ci char *buf) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 6538c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(core_id); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic struct attribute *mali_dp_attrs[] = { 6618c2ecf20Sopenharmony_ci &dev_attr_core_id.attr, 6628c2ecf20Sopenharmony_ci NULL, 6638c2ecf20Sopenharmony_ci}; 6648c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(mali_dp); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci#define MAX_OUTPUT_CHANNELS 3 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int malidp_runtime_pm_suspend(struct device *dev) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 6718c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 6728c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* we can only suspend if the hardware is in config mode */ 6758c2ecf20Sopenharmony_ci WARN_ON(!hwdev->hw->in_config_mode(hwdev)); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci malidp_se_irq_fini(hwdev); 6788c2ecf20Sopenharmony_ci malidp_de_irq_fini(hwdev); 6798c2ecf20Sopenharmony_ci hwdev->pm_suspended = true; 6808c2ecf20Sopenharmony_ci clk_disable_unprepare(hwdev->mclk); 6818c2ecf20Sopenharmony_ci clk_disable_unprepare(hwdev->aclk); 6828c2ecf20Sopenharmony_ci clk_disable_unprepare(hwdev->pclk); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return 0; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic int malidp_runtime_pm_resume(struct device *dev) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 6908c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 6918c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci clk_prepare_enable(hwdev->pclk); 6948c2ecf20Sopenharmony_ci clk_prepare_enable(hwdev->aclk); 6958c2ecf20Sopenharmony_ci clk_prepare_enable(hwdev->mclk); 6968c2ecf20Sopenharmony_ci hwdev->pm_suspended = false; 6978c2ecf20Sopenharmony_ci malidp_de_irq_hw_init(hwdev); 6988c2ecf20Sopenharmony_ci malidp_se_irq_hw_init(hwdev); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int malidp_bind(struct device *dev) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct resource *res; 7068c2ecf20Sopenharmony_ci struct drm_device *drm; 7078c2ecf20Sopenharmony_ci struct malidp_drm *malidp; 7088c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev; 7098c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7108c2ecf20Sopenharmony_ci struct of_device_id const *dev_id; 7118c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 7128c2ecf20Sopenharmony_ci /* number of lines for the R, G and B output */ 7138c2ecf20Sopenharmony_ci u8 output_width[MAX_OUTPUT_CHANNELS]; 7148c2ecf20Sopenharmony_ci int ret = 0, i; 7158c2ecf20Sopenharmony_ci u32 version, out_depth = 0; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL); 7188c2ecf20Sopenharmony_ci if (!malidp) 7198c2ecf20Sopenharmony_ci return -ENOMEM; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); 7228c2ecf20Sopenharmony_ci if (!hwdev) 7238c2ecf20Sopenharmony_ci return -ENOMEM; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); 7268c2ecf20Sopenharmony_ci malidp->dev = hwdev; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7298c2ecf20Sopenharmony_ci hwdev->regs = devm_ioremap_resource(dev, res); 7308c2ecf20Sopenharmony_ci if (IS_ERR(hwdev->regs)) 7318c2ecf20Sopenharmony_ci return PTR_ERR(hwdev->regs); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci hwdev->pclk = devm_clk_get(dev, "pclk"); 7348c2ecf20Sopenharmony_ci if (IS_ERR(hwdev->pclk)) 7358c2ecf20Sopenharmony_ci return PTR_ERR(hwdev->pclk); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci hwdev->aclk = devm_clk_get(dev, "aclk"); 7388c2ecf20Sopenharmony_ci if (IS_ERR(hwdev->aclk)) 7398c2ecf20Sopenharmony_ci return PTR_ERR(hwdev->aclk); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci hwdev->mclk = devm_clk_get(dev, "mclk"); 7428c2ecf20Sopenharmony_ci if (IS_ERR(hwdev->mclk)) 7438c2ecf20Sopenharmony_ci return PTR_ERR(hwdev->mclk); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); 7468c2ecf20Sopenharmony_ci if (IS_ERR(hwdev->pxlclk)) 7478c2ecf20Sopenharmony_ci return PTR_ERR(hwdev->pxlclk); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* Get the optional framebuffer memory resource */ 7508c2ecf20Sopenharmony_ci ret = of_reserved_mem_device_init(dev); 7518c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) 7528c2ecf20Sopenharmony_ci return ret; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci drm = drm_dev_alloc(&malidp_driver, dev); 7558c2ecf20Sopenharmony_ci if (IS_ERR(drm)) { 7568c2ecf20Sopenharmony_ci ret = PTR_ERR(drm); 7578c2ecf20Sopenharmony_ci goto alloc_fail; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci drm->dev_private = malidp; 7618c2ecf20Sopenharmony_ci dev_set_drvdata(dev, drm); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* Enable power management */ 7648c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* Resume device to enable the clocks */ 7678c2ecf20Sopenharmony_ci if (pm_runtime_enabled(dev)) 7688c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 7698c2ecf20Sopenharmony_ci else 7708c2ecf20Sopenharmony_ci malidp_runtime_pm_resume(dev); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci dev_id = of_match_device(malidp_drm_of_match, dev); 7738c2ecf20Sopenharmony_ci if (!dev_id) { 7748c2ecf20Sopenharmony_ci ret = -EINVAL; 7758c2ecf20Sopenharmony_ci goto query_hw_fail; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!malidp_has_sufficient_address_space(res, dev_id)) { 7798c2ecf20Sopenharmony_ci DRM_ERROR("Insufficient address space in device-tree.\n"); 7808c2ecf20Sopenharmony_ci ret = -EINVAL; 7818c2ecf20Sopenharmony_ci goto query_hw_fail; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { 7858c2ecf20Sopenharmony_ci ret = -EINVAL; 7868c2ecf20Sopenharmony_ci goto query_hw_fail; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ret = hwdev->hw->query_hw(hwdev); 7908c2ecf20Sopenharmony_ci if (ret) { 7918c2ecf20Sopenharmony_ci DRM_ERROR("Invalid HW configuration\n"); 7928c2ecf20Sopenharmony_ci goto query_hw_fail; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID); 7968c2ecf20Sopenharmony_ci DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, 7978c2ecf20Sopenharmony_ci (version >> 12) & 0xf, (version >> 8) & 0xf); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci malidp->core_id = version; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci ret = of_property_read_u32(dev->of_node, 8028c2ecf20Sopenharmony_ci "arm,malidp-arqos-value", 8038c2ecf20Sopenharmony_ci &hwdev->arqos_value); 8048c2ecf20Sopenharmony_ci if (ret) 8058c2ecf20Sopenharmony_ci hwdev->arqos_value = 0x0; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* set the number of lines used for output of RGB data */ 8088c2ecf20Sopenharmony_ci ret = of_property_read_u8_array(dev->of_node, 8098c2ecf20Sopenharmony_ci "arm,malidp-output-port-lines", 8108c2ecf20Sopenharmony_ci output_width, MAX_OUTPUT_CHANNELS); 8118c2ecf20Sopenharmony_ci if (ret) 8128c2ecf20Sopenharmony_ci goto query_hw_fail; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) 8158c2ecf20Sopenharmony_ci out_depth = (out_depth << 8) | (output_width[i] & 0xf); 8168c2ecf20Sopenharmony_ci malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); 8178c2ecf20Sopenharmony_ci hwdev->output_color_depth = out_depth; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); 8208c2ecf20Sopenharmony_ci init_waitqueue_head(&malidp->wq); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci ret = malidp_init(drm); 8238c2ecf20Sopenharmony_ci if (ret < 0) 8248c2ecf20Sopenharmony_ci goto query_hw_fail; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* Set the CRTC's port so that the encoder component can find it */ 8278c2ecf20Sopenharmony_ci malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ret = component_bind_all(dev, drm); 8308c2ecf20Sopenharmony_ci if (ret) { 8318c2ecf20Sopenharmony_ci DRM_ERROR("Failed to bind all components\n"); 8328c2ecf20Sopenharmony_ci goto bind_fail; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* We expect to have a maximum of two encoders one for the actual 8368c2ecf20Sopenharmony_ci * display and a virtual one for the writeback connector 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci WARN_ON(drm->mode_config.num_encoder > 2); 8398c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) { 8408c2ecf20Sopenharmony_ci encoder->possible_clones = 8418c2ecf20Sopenharmony_ci (1 << drm->mode_config.num_encoder) - 1; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ret = malidp_irq_init(pdev); 8458c2ecf20Sopenharmony_ci if (ret < 0) 8468c2ecf20Sopenharmony_ci goto irq_init_fail; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci drm->irq_enabled = true; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 8518c2ecf20Sopenharmony_ci if (ret < 0) { 8528c2ecf20Sopenharmony_ci DRM_ERROR("failed to initialise vblank\n"); 8538c2ecf20Sopenharmony_ci goto vblank_fail; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci pm_runtime_put(dev); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 8628c2ecf20Sopenharmony_ci if (ret) 8638c2ecf20Sopenharmony_ci goto register_fail; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 32); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci return 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ciregister_fail: 8708c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 8718c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 8728c2ecf20Sopenharmony_civblank_fail: 8738c2ecf20Sopenharmony_ci malidp_se_irq_fini(hwdev); 8748c2ecf20Sopenharmony_ci malidp_de_irq_fini(hwdev); 8758c2ecf20Sopenharmony_ci drm->irq_enabled = false; 8768c2ecf20Sopenharmony_ciirq_init_fail: 8778c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 8788c2ecf20Sopenharmony_ci component_unbind_all(dev, drm); 8798c2ecf20Sopenharmony_cibind_fail: 8808c2ecf20Sopenharmony_ci of_node_put(malidp->crtc.port); 8818c2ecf20Sopenharmony_ci malidp->crtc.port = NULL; 8828c2ecf20Sopenharmony_ci malidp_fini(drm); 8838c2ecf20Sopenharmony_ciquery_hw_fail: 8848c2ecf20Sopenharmony_ci pm_runtime_put(dev); 8858c2ecf20Sopenharmony_ci if (pm_runtime_enabled(dev)) 8868c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 8878c2ecf20Sopenharmony_ci else 8888c2ecf20Sopenharmony_ci malidp_runtime_pm_suspend(dev); 8898c2ecf20Sopenharmony_ci drm->dev_private = NULL; 8908c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 8918c2ecf20Sopenharmony_ci drm_dev_put(drm); 8928c2ecf20Sopenharmony_cialloc_fail: 8938c2ecf20Sopenharmony_ci of_reserved_mem_device_release(dev); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return ret; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic void malidp_unbind(struct device *dev) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 9018c2ecf20Sopenharmony_ci struct malidp_drm *malidp = drm->dev_private; 9028c2ecf20Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci drm_dev_unregister(drm); 9058c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 9068c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 9078c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 9088c2ecf20Sopenharmony_ci malidp_se_irq_fini(hwdev); 9098c2ecf20Sopenharmony_ci malidp_de_irq_fini(hwdev); 9108c2ecf20Sopenharmony_ci drm->irq_enabled = false; 9118c2ecf20Sopenharmony_ci component_unbind_all(dev, drm); 9128c2ecf20Sopenharmony_ci of_node_put(malidp->crtc.port); 9138c2ecf20Sopenharmony_ci malidp->crtc.port = NULL; 9148c2ecf20Sopenharmony_ci malidp_fini(drm); 9158c2ecf20Sopenharmony_ci pm_runtime_put(dev); 9168c2ecf20Sopenharmony_ci if (pm_runtime_enabled(dev)) 9178c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9188c2ecf20Sopenharmony_ci else 9198c2ecf20Sopenharmony_ci malidp_runtime_pm_suspend(dev); 9208c2ecf20Sopenharmony_ci drm->dev_private = NULL; 9218c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 9228c2ecf20Sopenharmony_ci drm_dev_put(drm); 9238c2ecf20Sopenharmony_ci of_reserved_mem_device_release(dev); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic const struct component_master_ops malidp_master_ops = { 9278c2ecf20Sopenharmony_ci .bind = malidp_bind, 9288c2ecf20Sopenharmony_ci .unbind = malidp_unbind, 9298c2ecf20Sopenharmony_ci}; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic int malidp_compare_dev(struct device *dev, void *data) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct device_node *np = data; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci return dev->of_node == np; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int malidp_platform_probe(struct platform_device *pdev) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct device_node *port; 9418c2ecf20Sopenharmony_ci struct component_match *match = NULL; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!pdev->dev.of_node) 9448c2ecf20Sopenharmony_ci return -ENODEV; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* there is only one output port inside each device, find it */ 9478c2ecf20Sopenharmony_ci port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); 9488c2ecf20Sopenharmony_ci if (!port) 9498c2ecf20Sopenharmony_ci return -ENODEV; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, 9528c2ecf20Sopenharmony_ci port); 9538c2ecf20Sopenharmony_ci of_node_put(port); 9548c2ecf20Sopenharmony_ci return component_master_add_with_match(&pdev->dev, &malidp_master_ops, 9558c2ecf20Sopenharmony_ci match); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int malidp_platform_remove(struct platform_device *pdev) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci component_master_del(&pdev->dev, &malidp_master_ops); 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic int __maybe_unused malidp_pm_suspend(struct device *dev) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(drm); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic int __maybe_unused malidp_pm_resume(struct device *dev) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci drm_mode_config_helper_resume(drm); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int __maybe_unused malidp_pm_suspend_late(struct device *dev) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(dev)) { 9838c2ecf20Sopenharmony_ci malidp_runtime_pm_suspend(dev); 9848c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int __maybe_unused malidp_pm_resume_early(struct device *dev) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci malidp_runtime_pm_resume(dev); 9928c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic const struct dev_pm_ops malidp_pm_ops = { 9978c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ 9988c2ecf20Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ 9998c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) 10008c2ecf20Sopenharmony_ci}; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic struct platform_driver malidp_platform_driver = { 10038c2ecf20Sopenharmony_ci .probe = malidp_platform_probe, 10048c2ecf20Sopenharmony_ci .remove = malidp_platform_remove, 10058c2ecf20Sopenharmony_ci .driver = { 10068c2ecf20Sopenharmony_ci .name = "mali-dp", 10078c2ecf20Sopenharmony_ci .pm = &malidp_pm_ops, 10088c2ecf20Sopenharmony_ci .of_match_table = malidp_drm_of_match, 10098c2ecf20Sopenharmony_ci .dev_groups = mali_dp_groups, 10108c2ecf20Sopenharmony_ci }, 10118c2ecf20Sopenharmony_ci}; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cimodule_platform_driver(malidp_platform_driver); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); 10168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM Mali DP DRM driver"); 10178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1018