1ad49f860SLiviu Dudau /* 2ad49f860SLiviu Dudau * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. 3ad49f860SLiviu Dudau * Author: Liviu Dudau <Liviu.Dudau@arm.com> 4ad49f860SLiviu Dudau * 5ad49f860SLiviu Dudau * This program is free software and is provided to you under the terms of the 6ad49f860SLiviu Dudau * GNU General Public License version 2 as published by the Free Software 7ad49f860SLiviu Dudau * Foundation, and any use by you of this program is subject to the terms 8ad49f860SLiviu Dudau * of such GNU licence. 9ad49f860SLiviu Dudau * 10ad49f860SLiviu Dudau * ARM Mali DP500/DP550/DP650 KMS/DRM driver 11ad49f860SLiviu Dudau */ 12ad49f860SLiviu Dudau 13ad49f860SLiviu Dudau #include <linux/module.h> 14ad49f860SLiviu Dudau #include <linux/clk.h> 15ad49f860SLiviu Dudau #include <linux/component.h> 16ad49f860SLiviu Dudau #include <linux/of_device.h> 17ad49f860SLiviu Dudau #include <linux/of_graph.h> 18ad49f860SLiviu Dudau #include <linux/of_reserved_mem.h> 1985f64218SLiviu Dudau #include <linux/pm_runtime.h> 20613c5c7fSAlexandru Gheorghe #include <linux/debugfs.h> 21ad49f860SLiviu Dudau 22ad49f860SLiviu Dudau #include <drm/drmP.h> 23ad49f860SLiviu Dudau #include <drm/drm_atomic.h> 24ad49f860SLiviu Dudau #include <drm/drm_atomic_helper.h> 25ad49f860SLiviu Dudau #include <drm/drm_crtc.h> 26ad49f860SLiviu Dudau #include <drm/drm_crtc_helper.h> 27bdecd835SNoralf Trønnes #include <drm/drm_fb_helper.h> 28ad49f860SLiviu Dudau #include <drm/drm_fb_cma_helper.h> 29ad49f860SLiviu Dudau #include <drm/drm_gem_cma_helper.h> 30783f7d97SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 31194b8799SNoralf Trønnes #include <drm/drm_modeset_helper.h> 32ad49f860SLiviu Dudau #include <drm/drm_of.h> 33ad49f860SLiviu Dudau 34ad49f860SLiviu Dudau #include "malidp_drv.h" 358cbc5cafSBrian Starkey #include "malidp_mw.h" 36ad49f860SLiviu Dudau #include "malidp_regs.h" 37ad49f860SLiviu Dudau #include "malidp_hw.h" 38ad49f860SLiviu Dudau 39ad49f860SLiviu Dudau #define MALIDP_CONF_VALID_TIMEOUT 250 40ad49f860SLiviu Dudau 4102725d31SMihail Atanassov static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, 4202725d31SMihail Atanassov u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) 4302725d31SMihail Atanassov { 4402725d31SMihail Atanassov int i; 4502725d31SMihail Atanassov /* Update all channels with a single gamma curve. */ 4602725d31SMihail Atanassov const u32 gamma_write_mask = GENMASK(18, 16); 4702725d31SMihail Atanassov /* 4802725d31SMihail Atanassov * Always write an entire table, so the address field in 4902725d31SMihail Atanassov * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask 5002725d31SMihail Atanassov * directly. 5102725d31SMihail Atanassov */ 5202725d31SMihail Atanassov malidp_hw_write(hwdev, gamma_write_mask, 53a6993b21SLiviu Dudau hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); 5402725d31SMihail Atanassov for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) 5502725d31SMihail Atanassov malidp_hw_write(hwdev, data[i], 56a6993b21SLiviu Dudau hwdev->hw->map.coeffs_base + 5702725d31SMihail Atanassov MALIDP_COEF_TABLE_DATA); 5802725d31SMihail Atanassov } 5902725d31SMihail Atanassov 6002725d31SMihail Atanassov static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc, 6102725d31SMihail Atanassov struct drm_crtc_state *old_state) 6202725d31SMihail Atanassov { 6302725d31SMihail Atanassov struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 6402725d31SMihail Atanassov struct malidp_hw_device *hwdev = malidp->dev; 6502725d31SMihail Atanassov 6602725d31SMihail Atanassov if (!crtc->state->color_mgmt_changed) 6702725d31SMihail Atanassov return; 6802725d31SMihail Atanassov 6902725d31SMihail Atanassov if (!crtc->state->gamma_lut) { 7002725d31SMihail Atanassov malidp_hw_clearbits(hwdev, 7102725d31SMihail Atanassov MALIDP_DISP_FUNC_GAMMA, 7202725d31SMihail Atanassov MALIDP_DE_DISPLAY_FUNC); 7302725d31SMihail Atanassov } else { 7402725d31SMihail Atanassov struct malidp_crtc_state *mc = 7502725d31SMihail Atanassov to_malidp_crtc_state(crtc->state); 7602725d31SMihail Atanassov 7702725d31SMihail Atanassov if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id != 7802725d31SMihail Atanassov old_state->gamma_lut->base.id)) 7902725d31SMihail Atanassov malidp_write_gamma_table(hwdev, mc->gamma_coeffs); 8002725d31SMihail Atanassov 8102725d31SMihail Atanassov malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA, 8202725d31SMihail Atanassov MALIDP_DE_DISPLAY_FUNC); 8302725d31SMihail Atanassov } 8402725d31SMihail Atanassov } 8502725d31SMihail Atanassov 866954f245SMihail Atanassov static 876954f245SMihail Atanassov void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, 886954f245SMihail Atanassov struct drm_crtc_state *old_state) 896954f245SMihail Atanassov { 906954f245SMihail Atanassov struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 916954f245SMihail Atanassov struct malidp_hw_device *hwdev = malidp->dev; 926954f245SMihail Atanassov int i; 936954f245SMihail Atanassov 946954f245SMihail Atanassov if (!crtc->state->color_mgmt_changed) 956954f245SMihail Atanassov return; 966954f245SMihail Atanassov 976954f245SMihail Atanassov if (!crtc->state->ctm) { 986954f245SMihail Atanassov malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ, 996954f245SMihail Atanassov MALIDP_DE_DISPLAY_FUNC); 1006954f245SMihail Atanassov } else { 1016954f245SMihail Atanassov struct malidp_crtc_state *mc = 1026954f245SMihail Atanassov to_malidp_crtc_state(crtc->state); 1036954f245SMihail Atanassov 1046954f245SMihail Atanassov if (!old_state->ctm || (crtc->state->ctm->base.id != 1056954f245SMihail Atanassov old_state->ctm->base.id)) 1066954f245SMihail Atanassov for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) 1076954f245SMihail Atanassov malidp_hw_write(hwdev, 1086954f245SMihail Atanassov mc->coloradj_coeffs[i], 109a6993b21SLiviu Dudau hwdev->hw->map.coeffs_base + 1106954f245SMihail Atanassov MALIDP_COLOR_ADJ_COEF + 4 * i); 1116954f245SMihail Atanassov 1126954f245SMihail Atanassov malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, 1136954f245SMihail Atanassov MALIDP_DE_DISPLAY_FUNC); 1146954f245SMihail Atanassov } 1156954f245SMihail Atanassov } 1166954f245SMihail Atanassov 11728ce675bSMihail Atanassov static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, 11828ce675bSMihail Atanassov struct drm_crtc_state *old_state) 11928ce675bSMihail Atanassov { 12028ce675bSMihail Atanassov struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state); 12128ce675bSMihail Atanassov struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state); 12228ce675bSMihail Atanassov struct malidp_drm *malidp = crtc_to_malidp_device(crtc); 12328ce675bSMihail Atanassov struct malidp_hw_device *hwdev = malidp->dev; 12428ce675bSMihail Atanassov struct malidp_se_config *s = &cs->scaler_config; 12528ce675bSMihail Atanassov struct malidp_se_config *old_s = &old_cs->scaler_config; 126a6993b21SLiviu Dudau u32 se_control = hwdev->hw->map.se_base + 127a6993b21SLiviu Dudau ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? 12828ce675bSMihail Atanassov 0x10 : 0xC); 12928ce675bSMihail Atanassov u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; 13028ce675bSMihail Atanassov u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; 13128ce675bSMihail Atanassov u32 val; 13228ce675bSMihail Atanassov 13328ce675bSMihail Atanassov /* Set SE_CONTROL */ 13428ce675bSMihail Atanassov if (!s->scale_enable) { 13528ce675bSMihail Atanassov val = malidp_hw_read(hwdev, se_control); 13628ce675bSMihail Atanassov val &= ~MALIDP_SE_SCALING_EN; 13728ce675bSMihail Atanassov malidp_hw_write(hwdev, val, se_control); 13828ce675bSMihail Atanassov return; 13928ce675bSMihail Atanassov } 14028ce675bSMihail Atanassov 141a6993b21SLiviu Dudau hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); 14228ce675bSMihail Atanassov val = malidp_hw_read(hwdev, se_control); 14328ce675bSMihail Atanassov val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; 14428ce675bSMihail Atanassov 1450274e6a0SMihail Atanassov val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK); 1460274e6a0SMihail Atanassov val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0; 1470274e6a0SMihail Atanassov 14828ce675bSMihail Atanassov val |= MALIDP_SE_RGBO_IF_EN; 14928ce675bSMihail Atanassov malidp_hw_write(hwdev, val, se_control); 15028ce675bSMihail Atanassov 15128ce675bSMihail Atanassov /* Set IN_SIZE & OUT_SIZE. */ 15228ce675bSMihail Atanassov val = MALIDP_SE_SET_V_SIZE(s->input_h) | 15328ce675bSMihail Atanassov MALIDP_SE_SET_H_SIZE(s->input_w); 15428ce675bSMihail Atanassov malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE); 15528ce675bSMihail Atanassov val = MALIDP_SE_SET_V_SIZE(s->output_h) | 15628ce675bSMihail Atanassov MALIDP_SE_SET_H_SIZE(s->output_w); 15728ce675bSMihail Atanassov malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE); 15828ce675bSMihail Atanassov 15928ce675bSMihail Atanassov /* Set phase regs. */ 16028ce675bSMihail Atanassov malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH); 16128ce675bSMihail Atanassov malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH); 16228ce675bSMihail Atanassov malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH); 16328ce675bSMihail Atanassov malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH); 16428ce675bSMihail Atanassov } 16528ce675bSMihail Atanassov 166ad49f860SLiviu Dudau /* 167ad49f860SLiviu Dudau * set the "config valid" bit and wait until the hardware acts on it 168ad49f860SLiviu Dudau */ 169ad49f860SLiviu Dudau static int malidp_set_and_wait_config_valid(struct drm_device *drm) 170ad49f860SLiviu Dudau { 171ad49f860SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 172ad49f860SLiviu Dudau struct malidp_hw_device *hwdev = malidp->dev; 173ad49f860SLiviu Dudau int ret; 174ad49f860SLiviu Dudau 1750735cfdfSLiviu Dudau hwdev->hw->set_config_valid(hwdev, 1); 176ad49f860SLiviu Dudau /* don't wait for config_valid flag if we are in config mode */ 1771cb3cbe7SLiviu Dudau if (hwdev->hw->in_config_mode(hwdev)) { 1781cb3cbe7SLiviu Dudau atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); 179ad49f860SLiviu Dudau return 0; 1801cb3cbe7SLiviu Dudau } 181ad49f860SLiviu Dudau 182ad49f860SLiviu Dudau ret = wait_event_interruptible_timeout(malidp->wq, 1831cb3cbe7SLiviu Dudau atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, 184ad49f860SLiviu Dudau msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); 185ad49f860SLiviu Dudau 186ad49f860SLiviu Dudau return (ret > 0) ? 0 : -ETIMEDOUT; 187ad49f860SLiviu Dudau } 188ad49f860SLiviu Dudau 189ad49f860SLiviu Dudau static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) 190ad49f860SLiviu Dudau { 191ad49f860SLiviu Dudau struct drm_device *drm = state->dev; 192ad49f860SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 193ad49f860SLiviu Dudau 194d862b2d6SLiviu Dudau malidp->event = malidp->crtc.state->event; 195ad49f860SLiviu Dudau malidp->crtc.state->event = NULL; 196ad49f860SLiviu Dudau 197d862b2d6SLiviu Dudau if (malidp->crtc.state->active) { 198d862b2d6SLiviu Dudau /* 199d862b2d6SLiviu Dudau * if we have an event to deliver to userspace, make sure 200d862b2d6SLiviu Dudau * the vblank is enabled as we are sending it from the IRQ 201d862b2d6SLiviu Dudau * handler. 202d862b2d6SLiviu Dudau */ 203d862b2d6SLiviu Dudau if (malidp->event) 204d862b2d6SLiviu Dudau drm_crtc_vblank_get(&malidp->crtc); 205d862b2d6SLiviu Dudau 206d862b2d6SLiviu Dudau /* only set config_valid if the CRTC is enabled */ 207d862b2d6SLiviu Dudau if (malidp_set_and_wait_config_valid(drm) < 0) 208d862b2d6SLiviu Dudau DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); 209d862b2d6SLiviu Dudau } else if (malidp->event) { 210d862b2d6SLiviu Dudau /* CRTC inactive means vblank IRQ is disabled, send event directly */ 211ad49f860SLiviu Dudau spin_lock_irq(&drm->event_lock); 212d862b2d6SLiviu Dudau drm_crtc_send_vblank_event(&malidp->crtc, malidp->event); 213d862b2d6SLiviu Dudau malidp->event = NULL; 214ad49f860SLiviu Dudau spin_unlock_irq(&drm->event_lock); 215ad49f860SLiviu Dudau } 216ad49f860SLiviu Dudau drm_atomic_helper_commit_hw_done(state); 217ad49f860SLiviu Dudau } 218ad49f860SLiviu Dudau 219ad49f860SLiviu Dudau static void malidp_atomic_commit_tail(struct drm_atomic_state *state) 220ad49f860SLiviu Dudau { 221ad49f860SLiviu Dudau struct drm_device *drm = state->dev; 2221cb3cbe7SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 22302725d31SMihail Atanassov struct drm_crtc *crtc; 22402725d31SMihail Atanassov struct drm_crtc_state *old_crtc_state; 22502725d31SMihail Atanassov int i; 226ad49f860SLiviu Dudau 22785f64218SLiviu Dudau pm_runtime_get_sync(drm->dev); 22885f64218SLiviu Dudau 2291cb3cbe7SLiviu Dudau /* 2301cb3cbe7SLiviu Dudau * set config_valid to a special value to let IRQ handlers 2311cb3cbe7SLiviu Dudau * know that we are updating registers 2321cb3cbe7SLiviu Dudau */ 2331cb3cbe7SLiviu Dudau atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); 2340735cfdfSLiviu Dudau malidp->dev->hw->set_config_valid(malidp->dev, 0); 2351cb3cbe7SLiviu Dudau 236ad49f860SLiviu Dudau drm_atomic_helper_commit_modeset_disables(drm, state); 23746f1d42fSLiviu Dudau 238a8e3fb55SMaarten Lankhorst for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { 23902725d31SMihail Atanassov malidp_atomic_commit_update_gamma(crtc, old_crtc_state); 2406954f245SMihail Atanassov malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); 24128ce675bSMihail Atanassov malidp_atomic_commit_se_config(crtc, old_crtc_state); 2426954f245SMihail Atanassov } 24302725d31SMihail Atanassov 2448cbc5cafSBrian Starkey drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); 2458cbc5cafSBrian Starkey 2468cbc5cafSBrian Starkey malidp_mw_atomic_commit(drm, state); 247ad49f860SLiviu Dudau 24846f1d42fSLiviu Dudau drm_atomic_helper_commit_modeset_enables(drm, state); 24946f1d42fSLiviu Dudau 250ad49f860SLiviu Dudau malidp_atomic_commit_hw_done(state); 251ad49f860SLiviu Dudau 25285f64218SLiviu Dudau pm_runtime_put(drm->dev); 25385f64218SLiviu Dudau 254ad49f860SLiviu Dudau drm_atomic_helper_cleanup_planes(drm, state); 255ad49f860SLiviu Dudau } 256ad49f860SLiviu Dudau 257a4b10cceSLaurent Pinchart static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { 258ad49f860SLiviu Dudau .atomic_commit_tail = malidp_atomic_commit_tail, 259ad49f860SLiviu Dudau }; 260ad49f860SLiviu Dudau 261ad49f860SLiviu Dudau static const struct drm_mode_config_funcs malidp_mode_config_funcs = { 262783f7d97SNoralf Trønnes .fb_create = drm_gem_fb_create, 263ad49f860SLiviu Dudau .atomic_check = drm_atomic_helper_check, 264ad49f860SLiviu Dudau .atomic_commit = drm_atomic_helper_commit, 265ad49f860SLiviu Dudau }; 266ad49f860SLiviu Dudau 267ad49f860SLiviu Dudau static int malidp_init(struct drm_device *drm) 268ad49f860SLiviu Dudau { 269ad49f860SLiviu Dudau int ret; 270ad49f860SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 271ad49f860SLiviu Dudau struct malidp_hw_device *hwdev = malidp->dev; 272ad49f860SLiviu Dudau 273ad49f860SLiviu Dudau drm_mode_config_init(drm); 274ad49f860SLiviu Dudau 275ad49f860SLiviu Dudau drm->mode_config.min_width = hwdev->min_line_size; 276ad49f860SLiviu Dudau drm->mode_config.min_height = hwdev->min_line_size; 277ad49f860SLiviu Dudau drm->mode_config.max_width = hwdev->max_line_size; 278ad49f860SLiviu Dudau drm->mode_config.max_height = hwdev->max_line_size; 279ad49f860SLiviu Dudau drm->mode_config.funcs = &malidp_mode_config_funcs; 280ad49f860SLiviu Dudau drm->mode_config.helper_private = &malidp_mode_config_helpers; 281ad49f860SLiviu Dudau 282ad49f860SLiviu Dudau ret = malidp_crtc_init(drm); 2838cbc5cafSBrian Starkey if (ret) 2848cbc5cafSBrian Starkey goto crtc_fail; 2858cbc5cafSBrian Starkey 2868cbc5cafSBrian Starkey ret = malidp_mw_connector_init(drm); 2878cbc5cafSBrian Starkey if (ret) 2888cbc5cafSBrian Starkey goto crtc_fail; 289ad49f860SLiviu Dudau 290ad49f860SLiviu Dudau return 0; 2918cbc5cafSBrian Starkey 2928cbc5cafSBrian Starkey crtc_fail: 2938cbc5cafSBrian Starkey drm_mode_config_cleanup(drm); 2948cbc5cafSBrian Starkey return ret; 295ad49f860SLiviu Dudau } 296ad49f860SLiviu Dudau 297de9c4810SBrian Starkey static void malidp_fini(struct drm_device *drm) 298de9c4810SBrian Starkey { 299de9c4810SBrian Starkey drm_mode_config_cleanup(drm); 300de9c4810SBrian Starkey } 301de9c4810SBrian Starkey 302ad49f860SLiviu Dudau static int malidp_irq_init(struct platform_device *pdev) 303ad49f860SLiviu Dudau { 304ad49f860SLiviu Dudau int irq_de, irq_se, ret = 0; 305ad49f860SLiviu Dudau struct drm_device *drm = dev_get_drvdata(&pdev->dev); 30662862cfbSAyan Kumar Halder struct malidp_drm *malidp = drm->dev_private; 30762862cfbSAyan Kumar Halder struct malidp_hw_device *hwdev = malidp->dev; 308ad49f860SLiviu Dudau 309ad49f860SLiviu Dudau /* fetch the interrupts from DT */ 310ad49f860SLiviu Dudau irq_de = platform_get_irq_byname(pdev, "DE"); 311ad49f860SLiviu Dudau if (irq_de < 0) { 312ad49f860SLiviu Dudau DRM_ERROR("no 'DE' IRQ specified!\n"); 313ad49f860SLiviu Dudau return irq_de; 314ad49f860SLiviu Dudau } 315ad49f860SLiviu Dudau irq_se = platform_get_irq_byname(pdev, "SE"); 316ad49f860SLiviu Dudau if (irq_se < 0) { 317ad49f860SLiviu Dudau DRM_ERROR("no 'SE' IRQ specified!\n"); 318ad49f860SLiviu Dudau return irq_se; 319ad49f860SLiviu Dudau } 320ad49f860SLiviu Dudau 321ad49f860SLiviu Dudau ret = malidp_de_irq_init(drm, irq_de); 322ad49f860SLiviu Dudau if (ret) 323ad49f860SLiviu Dudau return ret; 324ad49f860SLiviu Dudau 325ad49f860SLiviu Dudau ret = malidp_se_irq_init(drm, irq_se); 326ad49f860SLiviu Dudau if (ret) { 32762862cfbSAyan Kumar Halder malidp_de_irq_fini(hwdev); 328ad49f860SLiviu Dudau return ret; 329ad49f860SLiviu Dudau } 330ad49f860SLiviu Dudau 331ad49f860SLiviu Dudau return 0; 332ad49f860SLiviu Dudau } 333ad49f860SLiviu Dudau 334d55f7e5dSDaniel Vetter DEFINE_DRM_GEM_CMA_FOPS(fops); 335ad49f860SLiviu Dudau 3365ed4fdfaSLiviu Dudau static int malidp_dumb_create(struct drm_file *file_priv, 3375ed4fdfaSLiviu Dudau struct drm_device *drm, 3385ed4fdfaSLiviu Dudau struct drm_mode_create_dumb *args) 3395ed4fdfaSLiviu Dudau { 3405ed4fdfaSLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 3415ed4fdfaSLiviu Dudau /* allocate for the worst case scenario, i.e. rotated buffers */ 3425ed4fdfaSLiviu Dudau u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1); 3435ed4fdfaSLiviu Dudau 3445ed4fdfaSLiviu Dudau args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); 3455ed4fdfaSLiviu Dudau 3465ed4fdfaSLiviu Dudau return drm_gem_cma_dumb_create_internal(file_priv, drm, args); 3475ed4fdfaSLiviu Dudau } 3485ed4fdfaSLiviu Dudau 349613c5c7fSAlexandru Gheorghe #ifdef CONFIG_DEBUG_FS 350613c5c7fSAlexandru Gheorghe 351613c5c7fSAlexandru Gheorghe static void malidp_error_stats_init(struct malidp_error_stats *error_stats) 352613c5c7fSAlexandru Gheorghe { 353613c5c7fSAlexandru Gheorghe error_stats->num_errors = 0; 354613c5c7fSAlexandru Gheorghe error_stats->last_error_status = 0; 355613c5c7fSAlexandru Gheorghe error_stats->last_error_vblank = -1; 356613c5c7fSAlexandru Gheorghe } 357613c5c7fSAlexandru Gheorghe 358613c5c7fSAlexandru Gheorghe void malidp_error(struct malidp_drm *malidp, 359613c5c7fSAlexandru Gheorghe struct malidp_error_stats *error_stats, u32 status, 360613c5c7fSAlexandru Gheorghe u64 vblank) 361613c5c7fSAlexandru Gheorghe { 362613c5c7fSAlexandru Gheorghe unsigned long irqflags; 363613c5c7fSAlexandru Gheorghe 364613c5c7fSAlexandru Gheorghe spin_lock_irqsave(&malidp->errors_lock, irqflags); 365613c5c7fSAlexandru Gheorghe error_stats->last_error_status = status; 366613c5c7fSAlexandru Gheorghe error_stats->last_error_vblank = vblank; 367613c5c7fSAlexandru Gheorghe error_stats->num_errors++; 368613c5c7fSAlexandru Gheorghe spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 369613c5c7fSAlexandru Gheorghe } 370613c5c7fSAlexandru Gheorghe 371613c5c7fSAlexandru Gheorghe void malidp_error_stats_dump(const char *prefix, 372613c5c7fSAlexandru Gheorghe struct malidp_error_stats error_stats, 373613c5c7fSAlexandru Gheorghe struct seq_file *m) 374613c5c7fSAlexandru Gheorghe { 375613c5c7fSAlexandru Gheorghe seq_printf(m, "[%s] num_errors : %d\n", prefix, 376613c5c7fSAlexandru Gheorghe error_stats.num_errors); 377613c5c7fSAlexandru Gheorghe seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, 378613c5c7fSAlexandru Gheorghe error_stats.last_error_status); 379613c5c7fSAlexandru Gheorghe seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, 380613c5c7fSAlexandru Gheorghe error_stats.last_error_vblank); 381613c5c7fSAlexandru Gheorghe } 382613c5c7fSAlexandru Gheorghe 383613c5c7fSAlexandru Gheorghe static int malidp_show_stats(struct seq_file *m, void *arg) 384613c5c7fSAlexandru Gheorghe { 385613c5c7fSAlexandru Gheorghe struct drm_device *drm = m->private; 386613c5c7fSAlexandru Gheorghe struct malidp_drm *malidp = drm->dev_private; 387613c5c7fSAlexandru Gheorghe unsigned long irqflags; 388613c5c7fSAlexandru Gheorghe struct malidp_error_stats de_errors, se_errors; 389613c5c7fSAlexandru Gheorghe 390613c5c7fSAlexandru Gheorghe spin_lock_irqsave(&malidp->errors_lock, irqflags); 391613c5c7fSAlexandru Gheorghe de_errors = malidp->de_errors; 392613c5c7fSAlexandru Gheorghe se_errors = malidp->se_errors; 393613c5c7fSAlexandru Gheorghe spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 394613c5c7fSAlexandru Gheorghe malidp_error_stats_dump("DE", de_errors, m); 395613c5c7fSAlexandru Gheorghe malidp_error_stats_dump("SE", se_errors, m); 396613c5c7fSAlexandru Gheorghe return 0; 397613c5c7fSAlexandru Gheorghe } 398613c5c7fSAlexandru Gheorghe 399613c5c7fSAlexandru Gheorghe static int malidp_debugfs_open(struct inode *inode, struct file *file) 400613c5c7fSAlexandru Gheorghe { 401613c5c7fSAlexandru Gheorghe return single_open(file, malidp_show_stats, inode->i_private); 402613c5c7fSAlexandru Gheorghe } 403613c5c7fSAlexandru Gheorghe 404613c5c7fSAlexandru Gheorghe static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, 405613c5c7fSAlexandru Gheorghe size_t len, loff_t *offp) 406613c5c7fSAlexandru Gheorghe { 407613c5c7fSAlexandru Gheorghe struct seq_file *m = file->private_data; 408613c5c7fSAlexandru Gheorghe struct drm_device *drm = m->private; 409613c5c7fSAlexandru Gheorghe struct malidp_drm *malidp = drm->dev_private; 410613c5c7fSAlexandru Gheorghe unsigned long irqflags; 411613c5c7fSAlexandru Gheorghe 412613c5c7fSAlexandru Gheorghe spin_lock_irqsave(&malidp->errors_lock, irqflags); 413613c5c7fSAlexandru Gheorghe malidp_error_stats_init(&malidp->de_errors); 414613c5c7fSAlexandru Gheorghe malidp_error_stats_init(&malidp->se_errors); 415613c5c7fSAlexandru Gheorghe spin_unlock_irqrestore(&malidp->errors_lock, irqflags); 416613c5c7fSAlexandru Gheorghe return len; 417613c5c7fSAlexandru Gheorghe } 418613c5c7fSAlexandru Gheorghe 419613c5c7fSAlexandru Gheorghe static const struct file_operations malidp_debugfs_fops = { 420613c5c7fSAlexandru Gheorghe .owner = THIS_MODULE, 421613c5c7fSAlexandru Gheorghe .open = malidp_debugfs_open, 422613c5c7fSAlexandru Gheorghe .read = seq_read, 423613c5c7fSAlexandru Gheorghe .write = malidp_debugfs_write, 424613c5c7fSAlexandru Gheorghe .llseek = seq_lseek, 425613c5c7fSAlexandru Gheorghe .release = single_release, 426613c5c7fSAlexandru Gheorghe }; 427613c5c7fSAlexandru Gheorghe 428613c5c7fSAlexandru Gheorghe static int malidp_debugfs_init(struct drm_minor *minor) 429613c5c7fSAlexandru Gheorghe { 430613c5c7fSAlexandru Gheorghe struct malidp_drm *malidp = minor->dev->dev_private; 431613c5c7fSAlexandru Gheorghe struct dentry *dentry = NULL; 432613c5c7fSAlexandru Gheorghe 433613c5c7fSAlexandru Gheorghe malidp_error_stats_init(&malidp->de_errors); 434613c5c7fSAlexandru Gheorghe malidp_error_stats_init(&malidp->se_errors); 435613c5c7fSAlexandru Gheorghe spin_lock_init(&malidp->errors_lock); 436613c5c7fSAlexandru Gheorghe dentry = debugfs_create_file("debug", 437613c5c7fSAlexandru Gheorghe S_IRUGO | S_IWUSR, 438613c5c7fSAlexandru Gheorghe minor->debugfs_root, minor->dev, 439613c5c7fSAlexandru Gheorghe &malidp_debugfs_fops); 440613c5c7fSAlexandru Gheorghe if (!dentry) { 441613c5c7fSAlexandru Gheorghe DRM_ERROR("Cannot create debug file\n"); 442613c5c7fSAlexandru Gheorghe return -ENOMEM; 443613c5c7fSAlexandru Gheorghe } 444613c5c7fSAlexandru Gheorghe return 0; 445613c5c7fSAlexandru Gheorghe } 446613c5c7fSAlexandru Gheorghe 447613c5c7fSAlexandru Gheorghe #endif //CONFIG_DEBUG_FS 448613c5c7fSAlexandru Gheorghe 449ad49f860SLiviu Dudau static struct drm_driver malidp_driver = { 450ad49f860SLiviu Dudau .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | 451ad49f860SLiviu Dudau DRIVER_PRIME, 452ad49f860SLiviu Dudau .gem_free_object_unlocked = drm_gem_cma_free_object, 453ad49f860SLiviu Dudau .gem_vm_ops = &drm_gem_cma_vm_ops, 4545ed4fdfaSLiviu Dudau .dumb_create = malidp_dumb_create, 455ad49f860SLiviu Dudau .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 456ad49f860SLiviu Dudau .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 457ad49f860SLiviu Dudau .gem_prime_export = drm_gem_prime_export, 458ad49f860SLiviu Dudau .gem_prime_import = drm_gem_prime_import, 459ad49f860SLiviu Dudau .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 460ad49f860SLiviu Dudau .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 461ad49f860SLiviu Dudau .gem_prime_vmap = drm_gem_cma_prime_vmap, 462ad49f860SLiviu Dudau .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 463ad49f860SLiviu Dudau .gem_prime_mmap = drm_gem_cma_prime_mmap, 464613c5c7fSAlexandru Gheorghe #ifdef CONFIG_DEBUG_FS 465613c5c7fSAlexandru Gheorghe .debugfs_init = malidp_debugfs_init, 466613c5c7fSAlexandru Gheorghe #endif 467ad49f860SLiviu Dudau .fops = &fops, 468ad49f860SLiviu Dudau .name = "mali-dp", 469ad49f860SLiviu Dudau .desc = "ARM Mali Display Processor driver", 470ad49f860SLiviu Dudau .date = "20160106", 471ad49f860SLiviu Dudau .major = 1, 472ad49f860SLiviu Dudau .minor = 0, 473ad49f860SLiviu Dudau }; 474ad49f860SLiviu Dudau 475ad49f860SLiviu Dudau static const struct of_device_id malidp_drm_of_match[] = { 476ad49f860SLiviu Dudau { 477ad49f860SLiviu Dudau .compatible = "arm,mali-dp500", 478ad49f860SLiviu Dudau .data = &malidp_device[MALIDP_500] 479ad49f860SLiviu Dudau }, 480ad49f860SLiviu Dudau { 481ad49f860SLiviu Dudau .compatible = "arm,mali-dp550", 482ad49f860SLiviu Dudau .data = &malidp_device[MALIDP_550] 483ad49f860SLiviu Dudau }, 484ad49f860SLiviu Dudau { 485ad49f860SLiviu Dudau .compatible = "arm,mali-dp650", 486ad49f860SLiviu Dudau .data = &malidp_device[MALIDP_650] 487ad49f860SLiviu Dudau }, 488ad49f860SLiviu Dudau {}, 489ad49f860SLiviu Dudau }; 490ad49f860SLiviu Dudau MODULE_DEVICE_TABLE(of, malidp_drm_of_match); 491ad49f860SLiviu Dudau 492592d8c8cSMihail Atanassov static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, 493592d8c8cSMihail Atanassov const struct of_device_id *dev_id) 494592d8c8cSMihail Atanassov { 495592d8c8cSMihail Atanassov u32 core_id; 496592d8c8cSMihail Atanassov const char *compatstr_dp500 = "arm,mali-dp500"; 497592d8c8cSMihail Atanassov bool is_dp500; 498592d8c8cSMihail Atanassov bool dt_is_dp500; 499592d8c8cSMihail Atanassov 500592d8c8cSMihail Atanassov /* 501592d8c8cSMihail Atanassov * The DP500 CORE_ID register is in a different location, so check it 502592d8c8cSMihail Atanassov * first. If the product id field matches, then this is DP500, otherwise 503592d8c8cSMihail Atanassov * check the DP550/650 CORE_ID register. 504592d8c8cSMihail Atanassov */ 505592d8c8cSMihail Atanassov core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); 506592d8c8cSMihail Atanassov /* Offset 0x18 will never read 0x500 on products other than DP500. */ 507592d8c8cSMihail Atanassov is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); 508592d8c8cSMihail Atanassov dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, 509592d8c8cSMihail Atanassov sizeof(dev_id->compatible)) != NULL; 510592d8c8cSMihail Atanassov if (is_dp500 != dt_is_dp500) { 511592d8c8cSMihail Atanassov DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", 512592d8c8cSMihail Atanassov dev_id->compatible, is_dp500 ? "is" : "is not"); 513592d8c8cSMihail Atanassov return false; 514592d8c8cSMihail Atanassov } else if (!dt_is_dp500) { 515592d8c8cSMihail Atanassov u16 product_id; 516592d8c8cSMihail Atanassov char buf[32]; 517592d8c8cSMihail Atanassov 518592d8c8cSMihail Atanassov core_id = malidp_hw_read(hwdev, 519592d8c8cSMihail Atanassov MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); 520592d8c8cSMihail Atanassov product_id = MALIDP_PRODUCT_ID(core_id); 521592d8c8cSMihail Atanassov snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); 522592d8c8cSMihail Atanassov if (!strnstr(dev_id->compatible, buf, 523592d8c8cSMihail Atanassov sizeof(dev_id->compatible))) { 524592d8c8cSMihail Atanassov DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", 525592d8c8cSMihail Atanassov dev_id->compatible, product_id); 526592d8c8cSMihail Atanassov return false; 527592d8c8cSMihail Atanassov } 528592d8c8cSMihail Atanassov } 529592d8c8cSMihail Atanassov return true; 530592d8c8cSMihail Atanassov } 531592d8c8cSMihail Atanassov 5324d6000edSMihail Atanassov static bool malidp_has_sufficient_address_space(const struct resource *res, 5334d6000edSMihail Atanassov const struct of_device_id *dev_id) 5344d6000edSMihail Atanassov { 5354d6000edSMihail Atanassov resource_size_t res_size = resource_size(res); 5364d6000edSMihail Atanassov const char *compatstr_dp500 = "arm,mali-dp500"; 5374d6000edSMihail Atanassov 5384d6000edSMihail Atanassov if (!strnstr(dev_id->compatible, compatstr_dp500, 5394d6000edSMihail Atanassov sizeof(dev_id->compatible))) 5404d6000edSMihail Atanassov return res_size >= MALIDP550_ADDR_SPACE_SIZE; 5414d6000edSMihail Atanassov else if (res_size < MALIDP500_ADDR_SPACE_SIZE) 5424d6000edSMihail Atanassov return false; 5434d6000edSMihail Atanassov return true; 5444d6000edSMihail Atanassov } 5454d6000edSMihail Atanassov 54650c7512fSLiviu Dudau static ssize_t core_id_show(struct device *dev, struct device_attribute *attr, 54750c7512fSLiviu Dudau char *buf) 54850c7512fSLiviu Dudau { 54950c7512fSLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 55050c7512fSLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 55150c7512fSLiviu Dudau 55250c7512fSLiviu Dudau return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); 55350c7512fSLiviu Dudau } 55450c7512fSLiviu Dudau 55550c7512fSLiviu Dudau DEVICE_ATTR_RO(core_id); 55650c7512fSLiviu Dudau 55750c7512fSLiviu Dudau static int malidp_init_sysfs(struct device *dev) 55850c7512fSLiviu Dudau { 55950c7512fSLiviu Dudau int ret = device_create_file(dev, &dev_attr_core_id); 56050c7512fSLiviu Dudau 56150c7512fSLiviu Dudau if (ret) 56250c7512fSLiviu Dudau DRM_ERROR("failed to create device file for core_id\n"); 56350c7512fSLiviu Dudau 56450c7512fSLiviu Dudau return ret; 56550c7512fSLiviu Dudau } 56650c7512fSLiviu Dudau 56750c7512fSLiviu Dudau static void malidp_fini_sysfs(struct device *dev) 56850c7512fSLiviu Dudau { 56950c7512fSLiviu Dudau device_remove_file(dev, &dev_attr_core_id); 57050c7512fSLiviu Dudau } 57150c7512fSLiviu Dudau 572ad49f860SLiviu Dudau #define MAX_OUTPUT_CHANNELS 3 573ad49f860SLiviu Dudau 57485f64218SLiviu Dudau static int malidp_runtime_pm_suspend(struct device *dev) 57585f64218SLiviu Dudau { 57685f64218SLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 57785f64218SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 57885f64218SLiviu Dudau struct malidp_hw_device *hwdev = malidp->dev; 57985f64218SLiviu Dudau 58085f64218SLiviu Dudau /* we can only suspend if the hardware is in config mode */ 581a6993b21SLiviu Dudau WARN_ON(!hwdev->hw->in_config_mode(hwdev)); 58285f64218SLiviu Dudau 583fbcc454eSAyan Kumar Halder malidp_se_irq_fini(hwdev); 584fbcc454eSAyan Kumar Halder malidp_de_irq_fini(hwdev); 58585f64218SLiviu Dudau hwdev->pm_suspended = true; 58685f64218SLiviu Dudau clk_disable_unprepare(hwdev->mclk); 58785f64218SLiviu Dudau clk_disable_unprepare(hwdev->aclk); 58885f64218SLiviu Dudau clk_disable_unprepare(hwdev->pclk); 58985f64218SLiviu Dudau 59085f64218SLiviu Dudau return 0; 59185f64218SLiviu Dudau } 59285f64218SLiviu Dudau 59385f64218SLiviu Dudau static int malidp_runtime_pm_resume(struct device *dev) 59485f64218SLiviu Dudau { 59585f64218SLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 59685f64218SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 59785f64218SLiviu Dudau struct malidp_hw_device *hwdev = malidp->dev; 59885f64218SLiviu Dudau 59985f64218SLiviu Dudau clk_prepare_enable(hwdev->pclk); 60085f64218SLiviu Dudau clk_prepare_enable(hwdev->aclk); 60185f64218SLiviu Dudau clk_prepare_enable(hwdev->mclk); 60285f64218SLiviu Dudau hwdev->pm_suspended = false; 603fbcc454eSAyan Kumar Halder malidp_de_irq_hw_init(hwdev); 604fbcc454eSAyan Kumar Halder malidp_se_irq_hw_init(hwdev); 60585f64218SLiviu Dudau 60685f64218SLiviu Dudau return 0; 60785f64218SLiviu Dudau } 60885f64218SLiviu Dudau 609ad49f860SLiviu Dudau static int malidp_bind(struct device *dev) 610ad49f860SLiviu Dudau { 611ad49f860SLiviu Dudau struct resource *res; 612ad49f860SLiviu Dudau struct drm_device *drm; 613ad49f860SLiviu Dudau struct malidp_drm *malidp; 614ad49f860SLiviu Dudau struct malidp_hw_device *hwdev; 615ad49f860SLiviu Dudau struct platform_device *pdev = to_platform_device(dev); 616592d8c8cSMihail Atanassov struct of_device_id const *dev_id; 6172e012e76SAlexandru Gheorghe struct drm_encoder *encoder; 618ad49f860SLiviu Dudau /* number of lines for the R, G and B output */ 619ad49f860SLiviu Dudau u8 output_width[MAX_OUTPUT_CHANNELS]; 620ad49f860SLiviu Dudau int ret = 0, i; 621ad49f860SLiviu Dudau u32 version, out_depth = 0; 622ad49f860SLiviu Dudau 623ad49f860SLiviu Dudau malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL); 624ad49f860SLiviu Dudau if (!malidp) 625ad49f860SLiviu Dudau return -ENOMEM; 626ad49f860SLiviu Dudau 627ad49f860SLiviu Dudau hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); 628ad49f860SLiviu Dudau if (!hwdev) 629ad49f860SLiviu Dudau return -ENOMEM; 630ad49f860SLiviu Dudau 631a6993b21SLiviu Dudau hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); 632ad49f860SLiviu Dudau malidp->dev = hwdev; 633ad49f860SLiviu Dudau 634ad49f860SLiviu Dudau res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 635ad49f860SLiviu Dudau hwdev->regs = devm_ioremap_resource(dev, res); 6361a9d71f8SWei Yongjun if (IS_ERR(hwdev->regs)) 637ad49f860SLiviu Dudau return PTR_ERR(hwdev->regs); 638ad49f860SLiviu Dudau 639ad49f860SLiviu Dudau hwdev->pclk = devm_clk_get(dev, "pclk"); 640ad49f860SLiviu Dudau if (IS_ERR(hwdev->pclk)) 641ad49f860SLiviu Dudau return PTR_ERR(hwdev->pclk); 642ad49f860SLiviu Dudau 643ad49f860SLiviu Dudau hwdev->aclk = devm_clk_get(dev, "aclk"); 644ad49f860SLiviu Dudau if (IS_ERR(hwdev->aclk)) 645ad49f860SLiviu Dudau return PTR_ERR(hwdev->aclk); 646ad49f860SLiviu Dudau 647ad49f860SLiviu Dudau hwdev->mclk = devm_clk_get(dev, "mclk"); 648ad49f860SLiviu Dudau if (IS_ERR(hwdev->mclk)) 649ad49f860SLiviu Dudau return PTR_ERR(hwdev->mclk); 650ad49f860SLiviu Dudau 651ad49f860SLiviu Dudau hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); 652ad49f860SLiviu Dudau if (IS_ERR(hwdev->pxlclk)) 653ad49f860SLiviu Dudau return PTR_ERR(hwdev->pxlclk); 654ad49f860SLiviu Dudau 655ad49f860SLiviu Dudau /* Get the optional framebuffer memory resource */ 656ad49f860SLiviu Dudau ret = of_reserved_mem_device_init(dev); 657ad49f860SLiviu Dudau if (ret && ret != -ENODEV) 658ad49f860SLiviu Dudau return ret; 659ad49f860SLiviu Dudau 660ad49f860SLiviu Dudau drm = drm_dev_alloc(&malidp_driver, dev); 6610f288605STom Gundersen if (IS_ERR(drm)) { 6620f288605STom Gundersen ret = PTR_ERR(drm); 663ad49f860SLiviu Dudau goto alloc_fail; 664ad49f860SLiviu Dudau } 665ad49f860SLiviu Dudau 66685f64218SLiviu Dudau drm->dev_private = malidp; 66785f64218SLiviu Dudau dev_set_drvdata(dev, drm); 66885f64218SLiviu Dudau 66985f64218SLiviu Dudau /* Enable power management */ 67085f64218SLiviu Dudau pm_runtime_enable(dev); 67185f64218SLiviu Dudau 67285f64218SLiviu Dudau /* Resume device to enable the clocks */ 67385f64218SLiviu Dudau if (pm_runtime_enabled(dev)) 67485f64218SLiviu Dudau pm_runtime_get_sync(dev); 67585f64218SLiviu Dudau else 67685f64218SLiviu Dudau malidp_runtime_pm_resume(dev); 677ad49f860SLiviu Dudau 678592d8c8cSMihail Atanassov dev_id = of_match_device(malidp_drm_of_match, dev); 679592d8c8cSMihail Atanassov if (!dev_id) { 680592d8c8cSMihail Atanassov ret = -EINVAL; 681592d8c8cSMihail Atanassov goto query_hw_fail; 682592d8c8cSMihail Atanassov } 683592d8c8cSMihail Atanassov 6844d6000edSMihail Atanassov if (!malidp_has_sufficient_address_space(res, dev_id)) { 6854d6000edSMihail Atanassov DRM_ERROR("Insufficient address space in device-tree.\n"); 6864d6000edSMihail Atanassov ret = -EINVAL; 6874d6000edSMihail Atanassov goto query_hw_fail; 6884d6000edSMihail Atanassov } 6894d6000edSMihail Atanassov 690592d8c8cSMihail Atanassov if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { 691592d8c8cSMihail Atanassov ret = -EINVAL; 692592d8c8cSMihail Atanassov goto query_hw_fail; 693592d8c8cSMihail Atanassov } 694592d8c8cSMihail Atanassov 695a6993b21SLiviu Dudau ret = hwdev->hw->query_hw(hwdev); 696ad49f860SLiviu Dudau if (ret) { 697ad49f860SLiviu Dudau DRM_ERROR("Invalid HW configuration\n"); 698ad49f860SLiviu Dudau goto query_hw_fail; 699ad49f860SLiviu Dudau } 700ad49f860SLiviu Dudau 701a6993b21SLiviu Dudau version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID); 702ad49f860SLiviu Dudau DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, 703ad49f860SLiviu Dudau (version >> 12) & 0xf, (version >> 8) & 0xf); 704ad49f860SLiviu Dudau 70550c7512fSLiviu Dudau malidp->core_id = version; 70650c7512fSLiviu Dudau 707ad49f860SLiviu Dudau /* set the number of lines used for output of RGB data */ 708ad49f860SLiviu Dudau ret = of_property_read_u8_array(dev->of_node, 709ad49f860SLiviu Dudau "arm,malidp-output-port-lines", 710ad49f860SLiviu Dudau output_width, MAX_OUTPUT_CHANNELS); 711ad49f860SLiviu Dudau if (ret) 712ad49f860SLiviu Dudau goto query_hw_fail; 713ad49f860SLiviu Dudau 714ad49f860SLiviu Dudau for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) 715ad49f860SLiviu Dudau out_depth = (out_depth << 8) | (output_width[i] & 0xf); 716a6993b21SLiviu Dudau malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); 717f877006dSAyan Kumar Halder hwdev->output_color_depth = out_depth; 718ad49f860SLiviu Dudau 7191cb3cbe7SLiviu Dudau atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); 720ad49f860SLiviu Dudau init_waitqueue_head(&malidp->wq); 721ad49f860SLiviu Dudau 722ad49f860SLiviu Dudau ret = malidp_init(drm); 723ad49f860SLiviu Dudau if (ret < 0) 72485f64218SLiviu Dudau goto query_hw_fail; 725ad49f860SLiviu Dudau 72650c7512fSLiviu Dudau ret = malidp_init_sysfs(dev); 72750c7512fSLiviu Dudau if (ret) 72850c7512fSLiviu Dudau goto init_fail; 72950c7512fSLiviu Dudau 730ad49f860SLiviu Dudau /* Set the CRTC's port so that the encoder component can find it */ 73186418f90SRob Herring malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); 732ad49f860SLiviu Dudau 733ad49f860SLiviu Dudau ret = component_bind_all(dev, drm); 734ad49f860SLiviu Dudau if (ret) { 735ad49f860SLiviu Dudau DRM_ERROR("Failed to bind all components\n"); 736ad49f860SLiviu Dudau goto bind_fail; 737ad49f860SLiviu Dudau } 738ad49f860SLiviu Dudau 7392e012e76SAlexandru Gheorghe /* We expect to have a maximum of two encoders one for the actual 7402e012e76SAlexandru Gheorghe * display and a virtual one for the writeback connector 7412e012e76SAlexandru Gheorghe */ 7422e012e76SAlexandru Gheorghe WARN_ON(drm->mode_config.num_encoder > 2); 7432e012e76SAlexandru Gheorghe list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) { 7442e012e76SAlexandru Gheorghe encoder->possible_clones = 7452e012e76SAlexandru Gheorghe (1 << drm->mode_config.num_encoder) - 1; 7462e012e76SAlexandru Gheorghe } 7472e012e76SAlexandru Gheorghe 748ad49f860SLiviu Dudau ret = malidp_irq_init(pdev); 749ad49f860SLiviu Dudau if (ret < 0) 750ad49f860SLiviu Dudau goto irq_init_fail; 751ad49f860SLiviu Dudau 752a6a7b9a2SLiviu Dudau drm->irq_enabled = true; 753a6a7b9a2SLiviu Dudau 754ad49f860SLiviu Dudau ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 755cabce634SAlexandru Gheorghe drm_crtc_vblank_reset(&malidp->crtc); 756ad49f860SLiviu Dudau if (ret < 0) { 757ad49f860SLiviu Dudau DRM_ERROR("failed to initialise vblank\n"); 758ad49f860SLiviu Dudau goto vblank_fail; 759ad49f860SLiviu Dudau } 76085f64218SLiviu Dudau pm_runtime_put(dev); 761ad49f860SLiviu Dudau 762ad49f860SLiviu Dudau drm_mode_config_reset(drm); 763ad49f860SLiviu Dudau 764ad49f860SLiviu Dudau drm_kms_helper_poll_init(drm); 76590731c24SBrian Starkey 76690731c24SBrian Starkey ret = drm_dev_register(drm, 0); 76790731c24SBrian Starkey if (ret) 76890731c24SBrian Starkey goto register_fail; 76990731c24SBrian Starkey 77095958098SNoralf Trønnes drm_fbdev_generic_setup(drm, 32); 77195958098SNoralf Trønnes 772ad49f860SLiviu Dudau return 0; 773ad49f860SLiviu Dudau 77490731c24SBrian Starkey register_fail: 77585f64218SLiviu Dudau drm_kms_helper_poll_fini(drm); 77685f64218SLiviu Dudau pm_runtime_get_sync(dev); 777ad49f860SLiviu Dudau vblank_fail: 77862862cfbSAyan Kumar Halder malidp_se_irq_fini(hwdev); 77962862cfbSAyan Kumar Halder malidp_de_irq_fini(hwdev); 780a6a7b9a2SLiviu Dudau drm->irq_enabled = false; 781ad49f860SLiviu Dudau irq_init_fail: 782109c4d18SAyan Kumar Halder drm_atomic_helper_shutdown(drm); 783ad49f860SLiviu Dudau component_unbind_all(dev, drm); 784ad49f860SLiviu Dudau bind_fail: 7853c31760eSBrian Starkey of_node_put(malidp->crtc.port); 7863c31760eSBrian Starkey malidp->crtc.port = NULL; 78750c7512fSLiviu Dudau init_fail: 78850c7512fSLiviu Dudau malidp_fini_sysfs(dev); 789de9c4810SBrian Starkey malidp_fini(drm); 79085f64218SLiviu Dudau query_hw_fail: 79185f64218SLiviu Dudau pm_runtime_put(dev); 79285f64218SLiviu Dudau if (pm_runtime_enabled(dev)) 79385f64218SLiviu Dudau pm_runtime_disable(dev); 79485f64218SLiviu Dudau else 79585f64218SLiviu Dudau malidp_runtime_pm_suspend(dev); 796ad49f860SLiviu Dudau drm->dev_private = NULL; 797ad49f860SLiviu Dudau dev_set_drvdata(dev, NULL); 7980970d7a2SSrishti Sharma drm_dev_put(drm); 799ad49f860SLiviu Dudau alloc_fail: 800ad49f860SLiviu Dudau of_reserved_mem_device_release(dev); 801ad49f860SLiviu Dudau 802ad49f860SLiviu Dudau return ret; 803ad49f860SLiviu Dudau } 804ad49f860SLiviu Dudau 805ad49f860SLiviu Dudau static void malidp_unbind(struct device *dev) 806ad49f860SLiviu Dudau { 807ad49f860SLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 808ad49f860SLiviu Dudau struct malidp_drm *malidp = drm->dev_private; 80962862cfbSAyan Kumar Halder struct malidp_hw_device *hwdev = malidp->dev; 810ad49f860SLiviu Dudau 81190731c24SBrian Starkey drm_dev_unregister(drm); 812ad49f860SLiviu Dudau drm_kms_helper_poll_fini(drm); 81385f64218SLiviu Dudau pm_runtime_get_sync(dev); 81457085dcaSLiviu Dudau drm_crtc_vblank_off(&malidp->crtc); 81562862cfbSAyan Kumar Halder malidp_se_irq_fini(hwdev); 81662862cfbSAyan Kumar Halder malidp_de_irq_fini(hwdev); 81757085dcaSLiviu Dudau drm->irq_enabled = false; 818109c4d18SAyan Kumar Halder drm_atomic_helper_shutdown(drm); 819ad49f860SLiviu Dudau component_unbind_all(dev, drm); 8203c31760eSBrian Starkey of_node_put(malidp->crtc.port); 8213c31760eSBrian Starkey malidp->crtc.port = NULL; 82250c7512fSLiviu Dudau malidp_fini_sysfs(dev); 823de9c4810SBrian Starkey malidp_fini(drm); 82485f64218SLiviu Dudau pm_runtime_put(dev); 82585f64218SLiviu Dudau if (pm_runtime_enabled(dev)) 82685f64218SLiviu Dudau pm_runtime_disable(dev); 82785f64218SLiviu Dudau else 82885f64218SLiviu Dudau malidp_runtime_pm_suspend(dev); 829ad49f860SLiviu Dudau drm->dev_private = NULL; 830ad49f860SLiviu Dudau dev_set_drvdata(dev, NULL); 8310970d7a2SSrishti Sharma drm_dev_put(drm); 832ad49f860SLiviu Dudau of_reserved_mem_device_release(dev); 833ad49f860SLiviu Dudau } 834ad49f860SLiviu Dudau 835ad49f860SLiviu Dudau static const struct component_master_ops malidp_master_ops = { 836ad49f860SLiviu Dudau .bind = malidp_bind, 837ad49f860SLiviu Dudau .unbind = malidp_unbind, 838ad49f860SLiviu Dudau }; 839ad49f860SLiviu Dudau 840ad49f860SLiviu Dudau static int malidp_compare_dev(struct device *dev, void *data) 841ad49f860SLiviu Dudau { 842ad49f860SLiviu Dudau struct device_node *np = data; 843ad49f860SLiviu Dudau 844ad49f860SLiviu Dudau return dev->of_node == np; 845ad49f860SLiviu Dudau } 846ad49f860SLiviu Dudau 847ad49f860SLiviu Dudau static int malidp_platform_probe(struct platform_device *pdev) 848ad49f860SLiviu Dudau { 84986418f90SRob Herring struct device_node *port; 850ad49f860SLiviu Dudau struct component_match *match = NULL; 851ad49f860SLiviu Dudau 852ad49f860SLiviu Dudau if (!pdev->dev.of_node) 853ad49f860SLiviu Dudau return -ENODEV; 854ad49f860SLiviu Dudau 855ad49f860SLiviu Dudau /* there is only one output port inside each device, find it */ 85686418f90SRob Herring port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); 85786418f90SRob Herring if (!port) 858ad49f860SLiviu Dudau return -ENODEV; 859ad49f860SLiviu Dudau 86097ac0e47SRussell King drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, 86197ac0e47SRussell King port); 86297ac0e47SRussell King of_node_put(port); 863ad49f860SLiviu Dudau return component_master_add_with_match(&pdev->dev, &malidp_master_ops, 864ad49f860SLiviu Dudau match); 865ad49f860SLiviu Dudau } 866ad49f860SLiviu Dudau 867ad49f860SLiviu Dudau static int malidp_platform_remove(struct platform_device *pdev) 868ad49f860SLiviu Dudau { 869ad49f860SLiviu Dudau component_master_del(&pdev->dev, &malidp_master_ops); 870ad49f860SLiviu Dudau return 0; 871ad49f860SLiviu Dudau } 872ad49f860SLiviu Dudau 87385f64218SLiviu Dudau static int __maybe_unused malidp_pm_suspend(struct device *dev) 87485f64218SLiviu Dudau { 87585f64218SLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 87685f64218SLiviu Dudau 877194b8799SNoralf Trønnes return drm_mode_config_helper_suspend(drm); 87885f64218SLiviu Dudau } 87985f64218SLiviu Dudau 88085f64218SLiviu Dudau static int __maybe_unused malidp_pm_resume(struct device *dev) 88185f64218SLiviu Dudau { 88285f64218SLiviu Dudau struct drm_device *drm = dev_get_drvdata(dev); 88385f64218SLiviu Dudau 884194b8799SNoralf Trønnes drm_mode_config_helper_resume(drm); 88585f64218SLiviu Dudau 88685f64218SLiviu Dudau return 0; 88785f64218SLiviu Dudau } 88885f64218SLiviu Dudau 889e368fc75SAyan Kumar Halder static int __maybe_unused malidp_pm_suspend_late(struct device *dev) 890e368fc75SAyan Kumar Halder { 891e368fc75SAyan Kumar Halder if (!pm_runtime_status_suspended(dev)) { 892e368fc75SAyan Kumar Halder malidp_runtime_pm_suspend(dev); 893e368fc75SAyan Kumar Halder pm_runtime_set_suspended(dev); 894e368fc75SAyan Kumar Halder } 895e368fc75SAyan Kumar Halder return 0; 896e368fc75SAyan Kumar Halder } 897e368fc75SAyan Kumar Halder 898e368fc75SAyan Kumar Halder static int __maybe_unused malidp_pm_resume_early(struct device *dev) 899e368fc75SAyan Kumar Halder { 900e368fc75SAyan Kumar Halder malidp_runtime_pm_resume(dev); 901e368fc75SAyan Kumar Halder pm_runtime_set_active(dev); 902e368fc75SAyan Kumar Halder return 0; 903e368fc75SAyan Kumar Halder } 904e368fc75SAyan Kumar Halder 90585f64218SLiviu Dudau static const struct dev_pm_ops malidp_pm_ops = { 90685f64218SLiviu Dudau SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ 907e368fc75SAyan Kumar Halder SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ 90885f64218SLiviu Dudau SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) 90985f64218SLiviu Dudau }; 91085f64218SLiviu Dudau 911ad49f860SLiviu Dudau static struct platform_driver malidp_platform_driver = { 912ad49f860SLiviu Dudau .probe = malidp_platform_probe, 913ad49f860SLiviu Dudau .remove = malidp_platform_remove, 914ad49f860SLiviu Dudau .driver = { 915ad49f860SLiviu Dudau .name = "mali-dp", 91685f64218SLiviu Dudau .pm = &malidp_pm_ops, 917ad49f860SLiviu Dudau .of_match_table = malidp_drm_of_match, 918ad49f860SLiviu Dudau }, 919ad49f860SLiviu Dudau }; 920ad49f860SLiviu Dudau 921ad49f860SLiviu Dudau module_platform_driver(malidp_platform_driver); 922ad49f860SLiviu Dudau 923ad49f860SLiviu Dudau MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); 924ad49f860SLiviu Dudau MODULE_DESCRIPTION("ARM Mali DP DRM driver"); 925ad49f860SLiviu Dudau MODULE_LICENSE("GPL v2"); 926