162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. 462306a36Sopenharmony_ci * Author: Liviu Dudau <Liviu.Dudau@arm.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * ARM Mali DP500/DP550/DP650 KMS/DRM driver 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/component.h> 1262306a36Sopenharmony_ci#include <linux/of_device.h> 1362306a36Sopenharmony_ci#include <linux/of_graph.h> 1462306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/debugfs.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_atomic.h> 2062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2262306a36Sopenharmony_ci#include <drm/drm_drv.h> 2362306a36Sopenharmony_ci#include <drm/drm_fbdev_dma.h> 2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2562306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 2662306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 2762306a36Sopenharmony_ci#include <drm/drm_managed.h> 2862306a36Sopenharmony_ci#include <drm/drm_modeset_helper.h> 2962306a36Sopenharmony_ci#include <drm/drm_module.h> 3062306a36Sopenharmony_ci#include <drm/drm_of.h> 3162306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3262306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "malidp_drv.h" 3562306a36Sopenharmony_ci#include "malidp_mw.h" 3662306a36Sopenharmony_ci#include "malidp_regs.h" 3762306a36Sopenharmony_ci#include "malidp_hw.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MALIDP_CONF_VALID_TIMEOUT 250 4062306a36Sopenharmony_ci#define AFBC_HEADER_SIZE 16 4162306a36Sopenharmony_ci#define AFBC_SUPERBLK_ALIGNMENT 128 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void malidp_write_gamma_table(struct malidp_hw_device *hwdev, 4462306a36Sopenharmony_ci u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int i; 4762306a36Sopenharmony_ci /* Update all channels with a single gamma curve. */ 4862306a36Sopenharmony_ci const u32 gamma_write_mask = GENMASK(18, 16); 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * Always write an entire table, so the address field in 5162306a36Sopenharmony_ci * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask 5262306a36Sopenharmony_ci * directly. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci malidp_hw_write(hwdev, gamma_write_mask, 5562306a36Sopenharmony_ci hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); 5662306a36Sopenharmony_ci for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) 5762306a36Sopenharmony_ci malidp_hw_write(hwdev, data[i], 5862306a36Sopenharmony_ci hwdev->hw->map.coeffs_base + 5962306a36Sopenharmony_ci MALIDP_COEF_TABLE_DATA); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc, 6362306a36Sopenharmony_ci struct drm_crtc_state *old_state) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 6662306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!crtc->state->color_mgmt_changed) 6962306a36Sopenharmony_ci return; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!crtc->state->gamma_lut) { 7262306a36Sopenharmony_ci malidp_hw_clearbits(hwdev, 7362306a36Sopenharmony_ci MALIDP_DISP_FUNC_GAMMA, 7462306a36Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci struct malidp_crtc_state *mc = 7762306a36Sopenharmony_ci to_malidp_crtc_state(crtc->state); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id != 8062306a36Sopenharmony_ci old_state->gamma_lut->base.id)) 8162306a36Sopenharmony_ci malidp_write_gamma_table(hwdev, mc->gamma_coeffs); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA, 8462306a36Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic 8962306a36Sopenharmony_civoid malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, 9062306a36Sopenharmony_ci struct drm_crtc_state *old_state) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 9362306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 9462306a36Sopenharmony_ci int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!crtc->state->color_mgmt_changed) 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!crtc->state->ctm) { 10062306a36Sopenharmony_ci malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ, 10162306a36Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci struct malidp_crtc_state *mc = 10462306a36Sopenharmony_ci to_malidp_crtc_state(crtc->state); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!old_state->ctm || (crtc->state->ctm->base.id != 10762306a36Sopenharmony_ci old_state->ctm->base.id)) 10862306a36Sopenharmony_ci for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) 10962306a36Sopenharmony_ci malidp_hw_write(hwdev, 11062306a36Sopenharmony_ci mc->coloradj_coeffs[i], 11162306a36Sopenharmony_ci hwdev->hw->map.coeffs_base + 11262306a36Sopenharmony_ci MALIDP_COLOR_ADJ_COEF + 4 * i); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, 11562306a36Sopenharmony_ci MALIDP_DE_DISPLAY_FUNC); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void malidp_atomic_commit_se_config(struct drm_crtc *crtc, 12062306a36Sopenharmony_ci struct drm_crtc_state *old_state) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state); 12362306a36Sopenharmony_ci struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state); 12462306a36Sopenharmony_ci struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 12562306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 12662306a36Sopenharmony_ci struct malidp_se_config *s = &cs->scaler_config; 12762306a36Sopenharmony_ci struct malidp_se_config *old_s = &old_cs->scaler_config; 12862306a36Sopenharmony_ci u32 se_control = hwdev->hw->map.se_base + 12962306a36Sopenharmony_ci ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? 13062306a36Sopenharmony_ci 0x10 : 0xC); 13162306a36Sopenharmony_ci u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; 13262306a36Sopenharmony_ci u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; 13362306a36Sopenharmony_ci u32 val; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Set SE_CONTROL */ 13662306a36Sopenharmony_ci if (!s->scale_enable) { 13762306a36Sopenharmony_ci val = malidp_hw_read(hwdev, se_control); 13862306a36Sopenharmony_ci val &= ~MALIDP_SE_SCALING_EN; 13962306a36Sopenharmony_ci malidp_hw_write(hwdev, val, se_control); 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); 14462306a36Sopenharmony_ci val = malidp_hw_read(hwdev, se_control); 14562306a36Sopenharmony_ci val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK); 14862306a36Sopenharmony_ci val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci val |= MALIDP_SE_RGBO_IF_EN; 15162306a36Sopenharmony_ci malidp_hw_write(hwdev, val, se_control); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Set IN_SIZE & OUT_SIZE. */ 15462306a36Sopenharmony_ci val = MALIDP_SE_SET_V_SIZE(s->input_h) | 15562306a36Sopenharmony_ci MALIDP_SE_SET_H_SIZE(s->input_w); 15662306a36Sopenharmony_ci malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE); 15762306a36Sopenharmony_ci val = MALIDP_SE_SET_V_SIZE(s->output_h) | 15862306a36Sopenharmony_ci MALIDP_SE_SET_H_SIZE(s->output_w); 15962306a36Sopenharmony_ci malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Set phase regs. */ 16262306a36Sopenharmony_ci malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH); 16362306a36Sopenharmony_ci malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH); 16462306a36Sopenharmony_ci malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH); 16562306a36Sopenharmony_ci malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * set the "config valid" bit and wait until the hardware acts on it 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int malidp_set_and_wait_config_valid(struct drm_device *drm) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 17462306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hwdev->hw->set_config_valid(hwdev, 1); 17862306a36Sopenharmony_ci /* don't wait for config_valid flag if we are in config mode */ 17962306a36Sopenharmony_ci if (hwdev->hw->in_config_mode(hwdev)) { 18062306a36Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(malidp->wq, 18562306a36Sopenharmony_ci atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, 18662306a36Sopenharmony_ci msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return (ret > 0) ? 0 : -ETIMEDOUT; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct drm_device *drm = state->dev; 19462306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 19562306a36Sopenharmony_ci int loop = 5; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci malidp->event = malidp->crtc.state->event; 19862306a36Sopenharmony_ci malidp->crtc.state->event = NULL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (malidp->crtc.state->active) { 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * if we have an event to deliver to userspace, make sure 20362306a36Sopenharmony_ci * the vblank is enabled as we are sending it from the IRQ 20462306a36Sopenharmony_ci * handler. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci if (malidp->event) 20762306a36Sopenharmony_ci drm_crtc_vblank_get(&malidp->crtc); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* only set config_valid if the CRTC is enabled */ 21062306a36Sopenharmony_ci if (malidp_set_and_wait_config_valid(drm) < 0) { 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * make a loop around the second CVAL setting and 21362306a36Sopenharmony_ci * try 5 times before giving up. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci while (loop--) { 21662306a36Sopenharmony_ci if (!malidp_set_and_wait_config_valid(drm)) 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci } else if (malidp->event) { 22362306a36Sopenharmony_ci /* CRTC inactive means vblank IRQ is disabled, send event directly */ 22462306a36Sopenharmony_ci spin_lock_irq(&drm->event_lock); 22562306a36Sopenharmony_ci drm_crtc_send_vblank_event(&malidp->crtc, malidp->event); 22662306a36Sopenharmony_ci malidp->event = NULL; 22762306a36Sopenharmony_ci spin_unlock_irq(&drm->event_lock); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci drm_atomic_helper_commit_hw_done(state); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void malidp_atomic_commit_tail(struct drm_atomic_state *state) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct drm_device *drm = state->dev; 23562306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 23662306a36Sopenharmony_ci struct drm_crtc *crtc; 23762306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state; 23862306a36Sopenharmony_ci int i; 23962306a36Sopenharmony_ci bool fence_cookie = dma_fence_begin_signalling(); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci pm_runtime_get_sync(drm->dev); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * set config_valid to a special value to let IRQ handlers 24562306a36Sopenharmony_ci * know that we are updating registers 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); 24862306a36Sopenharmony_ci malidp->dev->hw->set_config_valid(malidp->dev, 0); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_disables(drm, state); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { 25362306a36Sopenharmony_ci malidp_atomic_commit_update_gamma(crtc, old_crtc_state); 25462306a36Sopenharmony_ci malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); 25562306a36Sopenharmony_ci malidp_atomic_commit_se_config(crtc, old_crtc_state); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci malidp_mw_atomic_commit(drm, state); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_enables(drm, state); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci malidp_atomic_commit_hw_done(state); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci dma_fence_end_signalling(fence_cookie); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pm_runtime_put(drm->dev); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci drm_atomic_helper_cleanup_planes(drm, state); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { 27462306a36Sopenharmony_ci .atomic_commit_tail = malidp_atomic_commit_tail, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic bool 27862306a36Sopenharmony_cimalidp_verify_afbc_framebuffer_caps(struct drm_device *dev, 27962306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci if (malidp_format_mod_supported(dev, mode_cmd->pixel_format, 28262306a36Sopenharmony_ci mode_cmd->modifier[0]) == false) 28362306a36Sopenharmony_ci return false; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (mode_cmd->offsets[0] != 0) { 28662306a36Sopenharmony_ci DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); 28762306a36Sopenharmony_ci return false; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { 29162306a36Sopenharmony_ci case AFBC_SIZE_16X16: 29262306a36Sopenharmony_ci if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { 29362306a36Sopenharmony_ci DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); 29462306a36Sopenharmony_ci return false; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci default: 29862306a36Sopenharmony_ci DRM_DEBUG_KMS("Unsupported AFBC block size\n"); 29962306a36Sopenharmony_ci return false; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return true; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic bool 30662306a36Sopenharmony_cimalidp_verify_afbc_framebuffer_size(struct drm_device *dev, 30762306a36Sopenharmony_ci struct drm_file *file, 30862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int n_superblocks = 0; 31162306a36Sopenharmony_ci const struct drm_format_info *info; 31262306a36Sopenharmony_ci struct drm_gem_object *objs = NULL; 31362306a36Sopenharmony_ci u32 afbc_superblock_size = 0, afbc_superblock_height = 0; 31462306a36Sopenharmony_ci u32 afbc_superblock_width = 0, afbc_size = 0; 31562306a36Sopenharmony_ci int bpp = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { 31862306a36Sopenharmony_ci case AFBC_SIZE_16X16: 31962306a36Sopenharmony_ci afbc_superblock_height = 16; 32062306a36Sopenharmony_ci afbc_superblock_width = 16; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci DRM_DEBUG_KMS("AFBC superblock size is not supported\n"); 32462306a36Sopenharmony_ci return false; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci info = drm_get_format_info(dev, mode_cmd); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci n_superblocks = (mode_cmd->width / afbc_superblock_width) * 33062306a36Sopenharmony_ci (mode_cmd->height / afbc_superblock_height); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci bpp = malidp_format_get_bpp(info->format); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height) 33562306a36Sopenharmony_ci / BITS_PER_BYTE; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); 33862306a36Sopenharmony_ci afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) { 34162306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) " 34262306a36Sopenharmony_ci "should be same as width (=%u) * bpp (=%u)\n", 34362306a36Sopenharmony_ci (mode_cmd->pitches[0] * BITS_PER_BYTE), 34462306a36Sopenharmony_ci mode_cmd->width, bpp); 34562306a36Sopenharmony_ci return false; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci objs = drm_gem_object_lookup(file, mode_cmd->handles[0]); 34962306a36Sopenharmony_ci if (!objs) { 35062306a36Sopenharmony_ci DRM_DEBUG_KMS("Failed to lookup GEM object\n"); 35162306a36Sopenharmony_ci return false; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (objs->size < afbc_size) { 35562306a36Sopenharmony_ci DRM_DEBUG_KMS("buffer size (%zu) too small for AFBC buffer size = %u\n", 35662306a36Sopenharmony_ci objs->size, afbc_size); 35762306a36Sopenharmony_ci drm_gem_object_put(objs); 35862306a36Sopenharmony_ci return false; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci drm_gem_object_put(objs); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return true; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic bool 36762306a36Sopenharmony_cimalidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file, 36862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci if (malidp_verify_afbc_framebuffer_caps(dev, mode_cmd)) 37162306a36Sopenharmony_ci return malidp_verify_afbc_framebuffer_size(dev, file, mode_cmd); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return false; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic struct drm_framebuffer * 37762306a36Sopenharmony_cimalidp_fb_create(struct drm_device *dev, struct drm_file *file, 37862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci if (mode_cmd->modifier[0]) { 38162306a36Sopenharmony_ci if (!malidp_verify_afbc_framebuffer(dev, file, mode_cmd)) 38262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return drm_gem_fb_create(dev, file, mode_cmd); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic const struct drm_mode_config_funcs malidp_mode_config_funcs = { 38962306a36Sopenharmony_ci .fb_create = malidp_fb_create, 39062306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 39162306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int malidp_init(struct drm_device *drm) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int ret; 39762306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 39862306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = drmm_mode_config_init(drm); 40162306a36Sopenharmony_ci if (ret) 40262306a36Sopenharmony_ci goto out; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci drm->mode_config.min_width = hwdev->min_line_size; 40562306a36Sopenharmony_ci drm->mode_config.min_height = hwdev->min_line_size; 40662306a36Sopenharmony_ci drm->mode_config.max_width = hwdev->max_line_size; 40762306a36Sopenharmony_ci drm->mode_config.max_height = hwdev->max_line_size; 40862306a36Sopenharmony_ci drm->mode_config.funcs = &malidp_mode_config_funcs; 40962306a36Sopenharmony_ci drm->mode_config.helper_private = &malidp_mode_config_helpers; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = malidp_crtc_init(drm); 41262306a36Sopenharmony_ci if (ret) 41362306a36Sopenharmony_ci goto out; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = malidp_mw_connector_init(drm); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci goto out; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciout: 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int malidp_irq_init(struct platform_device *pdev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int irq_de, irq_se, ret = 0; 42662306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(&pdev->dev); 42762306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 42862306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* fetch the interrupts from DT */ 43162306a36Sopenharmony_ci irq_de = platform_get_irq_byname(pdev, "DE"); 43262306a36Sopenharmony_ci if (irq_de < 0) { 43362306a36Sopenharmony_ci DRM_ERROR("no 'DE' IRQ specified!\n"); 43462306a36Sopenharmony_ci return irq_de; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci irq_se = platform_get_irq_byname(pdev, "SE"); 43762306a36Sopenharmony_ci if (irq_se < 0) { 43862306a36Sopenharmony_ci DRM_ERROR("no 'SE' IRQ specified!\n"); 43962306a36Sopenharmony_ci return irq_se; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = malidp_de_irq_init(drm, irq_de); 44362306a36Sopenharmony_ci if (ret) 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ret = malidp_se_irq_init(drm, irq_se); 44762306a36Sopenharmony_ci if (ret) { 44862306a36Sopenharmony_ci malidp_de_irq_fini(hwdev); 44962306a36Sopenharmony_ci return ret; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciDEFINE_DRM_GEM_DMA_FOPS(fops); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int malidp_dumb_create(struct drm_file *file_priv, 45862306a36Sopenharmony_ci struct drm_device *drm, 45962306a36Sopenharmony_ci struct drm_mode_create_dumb *args) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 46262306a36Sopenharmony_ci /* allocate for the worst case scenario, i.e. rotated buffers */ 46362306a36Sopenharmony_ci u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return drm_gem_dma_dumb_create_internal(file_priv, drm, args); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void malidp_error_stats_init(struct malidp_error_stats *error_stats) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci error_stats->num_errors = 0; 47562306a36Sopenharmony_ci error_stats->last_error_status = 0; 47662306a36Sopenharmony_ci error_stats->last_error_vblank = -1; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_civoid malidp_error(struct malidp_drm *malidp, 48062306a36Sopenharmony_ci struct malidp_error_stats *error_stats, u32 status, 48162306a36Sopenharmony_ci u64 vblank) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci unsigned long irqflags; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 48662306a36Sopenharmony_ci error_stats->last_error_status = status; 48762306a36Sopenharmony_ci error_stats->last_error_vblank = vblank; 48862306a36Sopenharmony_ci error_stats->num_errors++; 48962306a36Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void malidp_error_stats_dump(const char *prefix, 49362306a36Sopenharmony_ci struct malidp_error_stats error_stats, 49462306a36Sopenharmony_ci struct seq_file *m) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci seq_printf(m, "[%s] num_errors : %d\n", prefix, 49762306a36Sopenharmony_ci error_stats.num_errors); 49862306a36Sopenharmony_ci seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, 49962306a36Sopenharmony_ci error_stats.last_error_status); 50062306a36Sopenharmony_ci seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, 50162306a36Sopenharmony_ci error_stats.last_error_vblank); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int malidp_show_stats(struct seq_file *m, void *arg) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct drm_device *drm = m->private; 50762306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 50862306a36Sopenharmony_ci unsigned long irqflags; 50962306a36Sopenharmony_ci struct malidp_error_stats de_errors, se_errors; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 51262306a36Sopenharmony_ci de_errors = malidp->de_errors; 51362306a36Sopenharmony_ci se_errors = malidp->se_errors; 51462306a36Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 51562306a36Sopenharmony_ci malidp_error_stats_dump("DE", de_errors, m); 51662306a36Sopenharmony_ci malidp_error_stats_dump("SE", se_errors, m); 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int malidp_debugfs_open(struct inode *inode, struct file *file) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci return single_open(file, malidp_show_stats, inode->i_private); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, 52662306a36Sopenharmony_ci size_t len, loff_t *offp) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct seq_file *m = file->private_data; 52962306a36Sopenharmony_ci struct drm_device *drm = m->private; 53062306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 53162306a36Sopenharmony_ci unsigned long irqflags; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci spin_lock_irqsave(&malidp->errors_lock, irqflags); 53462306a36Sopenharmony_ci malidp_error_stats_init(&malidp->de_errors); 53562306a36Sopenharmony_ci malidp_error_stats_init(&malidp->se_errors); 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 53762306a36Sopenharmony_ci return len; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic const struct file_operations malidp_debugfs_fops = { 54162306a36Sopenharmony_ci .owner = THIS_MODULE, 54262306a36Sopenharmony_ci .open = malidp_debugfs_open, 54362306a36Sopenharmony_ci .read = seq_read, 54462306a36Sopenharmony_ci .write = malidp_debugfs_write, 54562306a36Sopenharmony_ci .llseek = seq_lseek, 54662306a36Sopenharmony_ci .release = single_release, 54762306a36Sopenharmony_ci}; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void malidp_debugfs_init(struct drm_minor *minor) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(minor->dev); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci malidp_error_stats_init(&malidp->de_errors); 55462306a36Sopenharmony_ci malidp_error_stats_init(&malidp->se_errors); 55562306a36Sopenharmony_ci spin_lock_init(&malidp->errors_lock); 55662306a36Sopenharmony_ci debugfs_create_file("debug", S_IRUGO | S_IWUSR, minor->debugfs_root, 55762306a36Sopenharmony_ci minor->dev, &malidp_debugfs_fops); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci#endif //CONFIG_DEBUG_FS 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic const struct drm_driver malidp_driver = { 56362306a36Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 56462306a36Sopenharmony_ci DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(malidp_dumb_create), 56562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 56662306a36Sopenharmony_ci .debugfs_init = malidp_debugfs_init, 56762306a36Sopenharmony_ci#endif 56862306a36Sopenharmony_ci .fops = &fops, 56962306a36Sopenharmony_ci .name = "mali-dp", 57062306a36Sopenharmony_ci .desc = "ARM Mali Display Processor driver", 57162306a36Sopenharmony_ci .date = "20160106", 57262306a36Sopenharmony_ci .major = 1, 57362306a36Sopenharmony_ci .minor = 0, 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic const struct of_device_id malidp_drm_of_match[] = { 57762306a36Sopenharmony_ci { 57862306a36Sopenharmony_ci .compatible = "arm,mali-dp500", 57962306a36Sopenharmony_ci .data = &malidp_device[MALIDP_500] 58062306a36Sopenharmony_ci }, 58162306a36Sopenharmony_ci { 58262306a36Sopenharmony_ci .compatible = "arm,mali-dp550", 58362306a36Sopenharmony_ci .data = &malidp_device[MALIDP_550] 58462306a36Sopenharmony_ci }, 58562306a36Sopenharmony_ci { 58662306a36Sopenharmony_ci .compatible = "arm,mali-dp650", 58762306a36Sopenharmony_ci .data = &malidp_device[MALIDP_650] 58862306a36Sopenharmony_ci }, 58962306a36Sopenharmony_ci {}, 59062306a36Sopenharmony_ci}; 59162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, malidp_drm_of_match); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, 59462306a36Sopenharmony_ci const struct of_device_id *dev_id) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci u32 core_id; 59762306a36Sopenharmony_ci const char *compatstr_dp500 = "arm,mali-dp500"; 59862306a36Sopenharmony_ci bool is_dp500; 59962306a36Sopenharmony_ci bool dt_is_dp500; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* 60262306a36Sopenharmony_ci * The DP500 CORE_ID register is in a different location, so check it 60362306a36Sopenharmony_ci * first. If the product id field matches, then this is DP500, otherwise 60462306a36Sopenharmony_ci * check the DP550/650 CORE_ID register. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); 60762306a36Sopenharmony_ci /* Offset 0x18 will never read 0x500 on products other than DP500. */ 60862306a36Sopenharmony_ci is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); 60962306a36Sopenharmony_ci dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, 61062306a36Sopenharmony_ci sizeof(dev_id->compatible)) != NULL; 61162306a36Sopenharmony_ci if (is_dp500 != dt_is_dp500) { 61262306a36Sopenharmony_ci DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", 61362306a36Sopenharmony_ci dev_id->compatible, is_dp500 ? "is" : "is not"); 61462306a36Sopenharmony_ci return false; 61562306a36Sopenharmony_ci } else if (!dt_is_dp500) { 61662306a36Sopenharmony_ci u16 product_id; 61762306a36Sopenharmony_ci char buf[32]; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci core_id = malidp_hw_read(hwdev, 62062306a36Sopenharmony_ci MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); 62162306a36Sopenharmony_ci product_id = MALIDP_PRODUCT_ID(core_id); 62262306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); 62362306a36Sopenharmony_ci if (!strnstr(dev_id->compatible, buf, 62462306a36Sopenharmony_ci sizeof(dev_id->compatible))) { 62562306a36Sopenharmony_ci DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", 62662306a36Sopenharmony_ci dev_id->compatible, product_id); 62762306a36Sopenharmony_ci return false; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci return true; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic bool malidp_has_sufficient_address_space(const struct resource *res, 63462306a36Sopenharmony_ci const struct of_device_id *dev_id) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci resource_size_t res_size = resource_size(res); 63762306a36Sopenharmony_ci const char *compatstr_dp500 = "arm,mali-dp500"; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (!strnstr(dev_id->compatible, compatstr_dp500, 64062306a36Sopenharmony_ci sizeof(dev_id->compatible))) 64162306a36Sopenharmony_ci return res_size >= MALIDP550_ADDR_SPACE_SIZE; 64262306a36Sopenharmony_ci else if (res_size < MALIDP500_ADDR_SPACE_SIZE) 64362306a36Sopenharmony_ci return false; 64462306a36Sopenharmony_ci return true; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic ssize_t core_id_show(struct device *dev, struct device_attribute *attr, 64862306a36Sopenharmony_ci char *buf) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 65162306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return sysfs_emit(buf, "%08x\n", malidp->core_id); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(core_id); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic struct attribute *mali_dp_attrs[] = { 65962306a36Sopenharmony_ci &dev_attr_core_id.attr, 66062306a36Sopenharmony_ci NULL, 66162306a36Sopenharmony_ci}; 66262306a36Sopenharmony_ciATTRIBUTE_GROUPS(mali_dp); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci#define MAX_OUTPUT_CHANNELS 3 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int malidp_runtime_pm_suspend(struct device *dev) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 66962306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 67062306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* we can only suspend if the hardware is in config mode */ 67362306a36Sopenharmony_ci WARN_ON(!hwdev->hw->in_config_mode(hwdev)); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci malidp_se_irq_fini(hwdev); 67662306a36Sopenharmony_ci malidp_de_irq_fini(hwdev); 67762306a36Sopenharmony_ci hwdev->pm_suspended = true; 67862306a36Sopenharmony_ci clk_disable_unprepare(hwdev->mclk); 67962306a36Sopenharmony_ci clk_disable_unprepare(hwdev->aclk); 68062306a36Sopenharmony_ci clk_disable_unprepare(hwdev->pclk); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic int malidp_runtime_pm_resume(struct device *dev) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 68862306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 68962306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci clk_prepare_enable(hwdev->pclk); 69262306a36Sopenharmony_ci clk_prepare_enable(hwdev->aclk); 69362306a36Sopenharmony_ci clk_prepare_enable(hwdev->mclk); 69462306a36Sopenharmony_ci hwdev->pm_suspended = false; 69562306a36Sopenharmony_ci malidp_de_irq_hw_init(hwdev); 69662306a36Sopenharmony_ci malidp_se_irq_hw_init(hwdev); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int malidp_bind(struct device *dev) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct resource *res; 70462306a36Sopenharmony_ci struct drm_device *drm; 70562306a36Sopenharmony_ci struct malidp_drm *malidp; 70662306a36Sopenharmony_ci struct malidp_hw_device *hwdev; 70762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 70862306a36Sopenharmony_ci struct of_device_id const *dev_id; 70962306a36Sopenharmony_ci struct drm_encoder *encoder; 71062306a36Sopenharmony_ci /* number of lines for the R, G and B output */ 71162306a36Sopenharmony_ci u8 output_width[MAX_OUTPUT_CHANNELS]; 71262306a36Sopenharmony_ci int ret = 0, i; 71362306a36Sopenharmony_ci u32 version, out_depth = 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci malidp = devm_drm_dev_alloc(dev, &malidp_driver, typeof(*malidp), base); 71662306a36Sopenharmony_ci if (IS_ERR(malidp)) 71762306a36Sopenharmony_ci return PTR_ERR(malidp); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci drm = &malidp->base; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci hwdev = drmm_kzalloc(drm, sizeof(*hwdev), GFP_KERNEL); 72262306a36Sopenharmony_ci if (!hwdev) 72362306a36Sopenharmony_ci return -ENOMEM; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); 72662306a36Sopenharmony_ci malidp->dev = hwdev; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci hwdev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 72962306a36Sopenharmony_ci if (IS_ERR(hwdev->regs)) 73062306a36Sopenharmony_ci return PTR_ERR(hwdev->regs); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci hwdev->pclk = devm_clk_get(dev, "pclk"); 73362306a36Sopenharmony_ci if (IS_ERR(hwdev->pclk)) 73462306a36Sopenharmony_ci return PTR_ERR(hwdev->pclk); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci hwdev->aclk = devm_clk_get(dev, "aclk"); 73762306a36Sopenharmony_ci if (IS_ERR(hwdev->aclk)) 73862306a36Sopenharmony_ci return PTR_ERR(hwdev->aclk); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci hwdev->mclk = devm_clk_get(dev, "mclk"); 74162306a36Sopenharmony_ci if (IS_ERR(hwdev->mclk)) 74262306a36Sopenharmony_ci return PTR_ERR(hwdev->mclk); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); 74562306a36Sopenharmony_ci if (IS_ERR(hwdev->pxlclk)) 74662306a36Sopenharmony_ci return PTR_ERR(hwdev->pxlclk); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Get the optional framebuffer memory resource */ 74962306a36Sopenharmony_ci ret = of_reserved_mem_device_init(dev); 75062306a36Sopenharmony_ci if (ret && ret != -ENODEV) 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci dev_set_drvdata(dev, drm); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Enable power management */ 75662306a36Sopenharmony_ci pm_runtime_enable(dev); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Resume device to enable the clocks */ 75962306a36Sopenharmony_ci if (pm_runtime_enabled(dev)) 76062306a36Sopenharmony_ci pm_runtime_get_sync(dev); 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci malidp_runtime_pm_resume(dev); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci dev_id = of_match_device(malidp_drm_of_match, dev); 76562306a36Sopenharmony_ci if (!dev_id) { 76662306a36Sopenharmony_ci ret = -EINVAL; 76762306a36Sopenharmony_ci goto query_hw_fail; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (!malidp_has_sufficient_address_space(res, dev_id)) { 77162306a36Sopenharmony_ci DRM_ERROR("Insufficient address space in device-tree.\n"); 77262306a36Sopenharmony_ci ret = -EINVAL; 77362306a36Sopenharmony_ci goto query_hw_fail; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { 77762306a36Sopenharmony_ci ret = -EINVAL; 77862306a36Sopenharmony_ci goto query_hw_fail; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci ret = hwdev->hw->query_hw(hwdev); 78262306a36Sopenharmony_ci if (ret) { 78362306a36Sopenharmony_ci DRM_ERROR("Invalid HW configuration\n"); 78462306a36Sopenharmony_ci goto query_hw_fail; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID); 78862306a36Sopenharmony_ci DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, 78962306a36Sopenharmony_ci (version >> 12) & 0xf, (version >> 8) & 0xf); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci malidp->core_id = version; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, 79462306a36Sopenharmony_ci "arm,malidp-arqos-value", 79562306a36Sopenharmony_ci &hwdev->arqos_value); 79662306a36Sopenharmony_ci if (ret) 79762306a36Sopenharmony_ci hwdev->arqos_value = 0x0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* set the number of lines used for output of RGB data */ 80062306a36Sopenharmony_ci ret = of_property_read_u8_array(dev->of_node, 80162306a36Sopenharmony_ci "arm,malidp-output-port-lines", 80262306a36Sopenharmony_ci output_width, MAX_OUTPUT_CHANNELS); 80362306a36Sopenharmony_ci if (ret) 80462306a36Sopenharmony_ci goto query_hw_fail; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) 80762306a36Sopenharmony_ci out_depth = (out_depth << 8) | (output_width[i] & 0xf); 80862306a36Sopenharmony_ci malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); 80962306a36Sopenharmony_ci hwdev->output_color_depth = out_depth; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); 81262306a36Sopenharmony_ci init_waitqueue_head(&malidp->wq); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = malidp_init(drm); 81562306a36Sopenharmony_ci if (ret < 0) 81662306a36Sopenharmony_ci goto query_hw_fail; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Set the CRTC's port so that the encoder component can find it */ 81962306a36Sopenharmony_ci malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ret = component_bind_all(dev, drm); 82262306a36Sopenharmony_ci if (ret) { 82362306a36Sopenharmony_ci DRM_ERROR("Failed to bind all components\n"); 82462306a36Sopenharmony_ci goto bind_fail; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* We expect to have a maximum of two encoders one for the actual 82862306a36Sopenharmony_ci * display and a virtual one for the writeback connector 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci WARN_ON(drm->mode_config.num_encoder > 2); 83162306a36Sopenharmony_ci list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) { 83262306a36Sopenharmony_ci encoder->possible_clones = 83362306a36Sopenharmony_ci (1 << drm->mode_config.num_encoder) - 1; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ret = malidp_irq_init(pdev); 83762306a36Sopenharmony_ci if (ret < 0) 83862306a36Sopenharmony_ci goto irq_init_fail; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 84162306a36Sopenharmony_ci if (ret < 0) { 84262306a36Sopenharmony_ci DRM_ERROR("failed to initialise vblank\n"); 84362306a36Sopenharmony_ci goto vblank_fail; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci pm_runtime_put(dev); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci drm_mode_config_reset(drm); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci drm_kms_helper_poll_init(drm); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ret = drm_dev_register(drm, 0); 85262306a36Sopenharmony_ci if (ret) 85362306a36Sopenharmony_ci goto register_fail; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci drm_fbdev_dma_setup(drm, 32); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ciregister_fail: 86062306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 86162306a36Sopenharmony_ci pm_runtime_get_sync(dev); 86262306a36Sopenharmony_civblank_fail: 86362306a36Sopenharmony_ci malidp_se_irq_fini(hwdev); 86462306a36Sopenharmony_ci malidp_de_irq_fini(hwdev); 86562306a36Sopenharmony_ciirq_init_fail: 86662306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 86762306a36Sopenharmony_ci component_unbind_all(dev, drm); 86862306a36Sopenharmony_cibind_fail: 86962306a36Sopenharmony_ci of_node_put(malidp->crtc.port); 87062306a36Sopenharmony_ci malidp->crtc.port = NULL; 87162306a36Sopenharmony_ciquery_hw_fail: 87262306a36Sopenharmony_ci pm_runtime_put(dev); 87362306a36Sopenharmony_ci if (pm_runtime_enabled(dev)) 87462306a36Sopenharmony_ci pm_runtime_disable(dev); 87562306a36Sopenharmony_ci else 87662306a36Sopenharmony_ci malidp_runtime_pm_suspend(dev); 87762306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 87862306a36Sopenharmony_ci of_reserved_mem_device_release(dev); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return ret; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void malidp_unbind(struct device *dev) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 88662306a36Sopenharmony_ci struct malidp_drm *malidp = drm_to_malidp(drm); 88762306a36Sopenharmony_ci struct malidp_hw_device *hwdev = malidp->dev; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci drm_dev_unregister(drm); 89062306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 89162306a36Sopenharmony_ci pm_runtime_get_sync(dev); 89262306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 89362306a36Sopenharmony_ci malidp_se_irq_fini(hwdev); 89462306a36Sopenharmony_ci malidp_de_irq_fini(hwdev); 89562306a36Sopenharmony_ci component_unbind_all(dev, drm); 89662306a36Sopenharmony_ci of_node_put(malidp->crtc.port); 89762306a36Sopenharmony_ci malidp->crtc.port = NULL; 89862306a36Sopenharmony_ci pm_runtime_put(dev); 89962306a36Sopenharmony_ci if (pm_runtime_enabled(dev)) 90062306a36Sopenharmony_ci pm_runtime_disable(dev); 90162306a36Sopenharmony_ci else 90262306a36Sopenharmony_ci malidp_runtime_pm_suspend(dev); 90362306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 90462306a36Sopenharmony_ci of_reserved_mem_device_release(dev); 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic const struct component_master_ops malidp_master_ops = { 90862306a36Sopenharmony_ci .bind = malidp_bind, 90962306a36Sopenharmony_ci .unbind = malidp_unbind, 91062306a36Sopenharmony_ci}; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int malidp_compare_dev(struct device *dev, void *data) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct device_node *np = data; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci return dev->of_node == np; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic int malidp_platform_probe(struct platform_device *pdev) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci struct device_node *port; 92262306a36Sopenharmony_ci struct component_match *match = NULL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!pdev->dev.of_node) 92562306a36Sopenharmony_ci return -ENODEV; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* there is only one output port inside each device, find it */ 92862306a36Sopenharmony_ci port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); 92962306a36Sopenharmony_ci if (!port) 93062306a36Sopenharmony_ci return -ENODEV; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, 93362306a36Sopenharmony_ci port); 93462306a36Sopenharmony_ci of_node_put(port); 93562306a36Sopenharmony_ci return component_master_add_with_match(&pdev->dev, &malidp_master_ops, 93662306a36Sopenharmony_ci match); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic void malidp_platform_remove(struct platform_device *pdev) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci component_master_del(&pdev->dev, &malidp_master_ops); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic int __maybe_unused malidp_pm_suspend(struct device *dev) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return drm_mode_config_helper_suspend(drm); 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int __maybe_unused malidp_pm_resume(struct device *dev) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci drm_mode_config_helper_resume(drm); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int __maybe_unused malidp_pm_suspend_late(struct device *dev) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci if (!pm_runtime_status_suspended(dev)) { 96362306a36Sopenharmony_ci malidp_runtime_pm_suspend(dev); 96462306a36Sopenharmony_ci pm_runtime_set_suspended(dev); 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int __maybe_unused malidp_pm_resume_early(struct device *dev) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci malidp_runtime_pm_resume(dev); 97262306a36Sopenharmony_ci pm_runtime_set_active(dev); 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic const struct dev_pm_ops malidp_pm_ops = { 97762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ 97862306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ 97962306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) 98062306a36Sopenharmony_ci}; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic struct platform_driver malidp_platform_driver = { 98362306a36Sopenharmony_ci .probe = malidp_platform_probe, 98462306a36Sopenharmony_ci .remove_new = malidp_platform_remove, 98562306a36Sopenharmony_ci .driver = { 98662306a36Sopenharmony_ci .name = "mali-dp", 98762306a36Sopenharmony_ci .pm = &malidp_pm_ops, 98862306a36Sopenharmony_ci .of_match_table = malidp_drm_of_match, 98962306a36Sopenharmony_ci .dev_groups = mali_dp_groups, 99062306a36Sopenharmony_ci }, 99162306a36Sopenharmony_ci}; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cidrm_module_platform_driver(malidp_platform_driver); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ciMODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); 99662306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM Mali DP DRM driver"); 99762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 998