18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Ingenic JZ47xx KMS driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "ingenic-drm.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/component.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 258c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_irq.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_managed.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_panel.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_plane.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct ingenic_dma_hwdesc { 408c2ecf20Sopenharmony_ci u32 next; 418c2ecf20Sopenharmony_ci u32 addr; 428c2ecf20Sopenharmony_ci u32 id; 438c2ecf20Sopenharmony_ci u32 cmd; 448c2ecf20Sopenharmony_ci} __packed; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct jz_soc_info { 478c2ecf20Sopenharmony_ci bool needs_dev_clk; 488c2ecf20Sopenharmony_ci bool has_osd; 498c2ecf20Sopenharmony_ci unsigned int max_width, max_height; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct ingenic_drm { 538c2ecf20Sopenharmony_ci struct drm_device drm; 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * f1 (aka. foreground1) is our primary plane, on top of which 568c2ecf20Sopenharmony_ci * f0 (aka. foreground0) can be overlayed. Z-order is fixed in 578c2ecf20Sopenharmony_ci * hardware and cannot be changed. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci struct drm_plane f0, f1, *ipu_plane; 608c2ecf20Sopenharmony_ci struct drm_crtc crtc; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci struct device *dev; 638c2ecf20Sopenharmony_ci struct regmap *map; 648c2ecf20Sopenharmony_ci struct clk *lcd_clk, *pix_clk; 658c2ecf20Sopenharmony_ci const struct jz_soc_info *soc_info; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci struct ingenic_dma_hwdesc *dma_hwdesc_f0, *dma_hwdesc_f1; 688c2ecf20Sopenharmony_ci dma_addr_t dma_hwdesc_phys_f0, dma_hwdesc_phys_f1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci bool panel_is_sharp; 718c2ecf20Sopenharmony_ci bool no_vblank; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * clk_mutex is used to synchronize the pixel clock rate update with 758c2ecf20Sopenharmony_ci * the VBLANK. When the pixel clock's parent clock needs to be updated, 768c2ecf20Sopenharmony_ci * clock_nb's notifier function will lock the mutex, then wait until the 778c2ecf20Sopenharmony_ci * next VBLANK. At that point, the parent clock's rate can be updated, 788c2ecf20Sopenharmony_ci * and the mutex is then unlocked. If an atomic commit happens in the 798c2ecf20Sopenharmony_ci * meantime, it will lock on the mutex, effectively waiting until the 808c2ecf20Sopenharmony_ci * clock update process finishes. Finally, the pixel clock's rate will 818c2ecf20Sopenharmony_ci * be recomputed when the mutex has been released, in the pending atomic 828c2ecf20Sopenharmony_ci * commit, or a future one. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci struct mutex clk_mutex; 858c2ecf20Sopenharmony_ci bool update_clk_rate; 868c2ecf20Sopenharmony_ci struct notifier_block clock_nb; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const u32 ingenic_drm_primary_formats[] = { 908c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB1555, 918c2ecf20Sopenharmony_ci DRM_FORMAT_RGB565, 928c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci switch (reg) { 988c2ecf20Sopenharmony_ci case JZ_REG_LCD_IID: 998c2ecf20Sopenharmony_ci case JZ_REG_LCD_SA0: 1008c2ecf20Sopenharmony_ci case JZ_REG_LCD_FID0: 1018c2ecf20Sopenharmony_ci case JZ_REG_LCD_CMD0: 1028c2ecf20Sopenharmony_ci case JZ_REG_LCD_SA1: 1038c2ecf20Sopenharmony_ci case JZ_REG_LCD_FID1: 1048c2ecf20Sopenharmony_ci case JZ_REG_LCD_CMD1: 1058c2ecf20Sopenharmony_ci return false; 1068c2ecf20Sopenharmony_ci default: 1078c2ecf20Sopenharmony_ci return true; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct regmap_config ingenic_drm_regmap_config = { 1128c2ecf20Sopenharmony_ci .reg_bits = 32, 1138c2ecf20Sopenharmony_ci .val_bits = 32, 1148c2ecf20Sopenharmony_ci .reg_stride = 4, 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci .max_register = JZ_REG_LCD_SIZE1, 1178c2ecf20Sopenharmony_ci .writeable_reg = ingenic_drm_writeable_reg, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic inline struct ingenic_drm *drm_device_get_priv(struct drm_device *drm) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return container_of(drm, struct ingenic_drm, drm); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return container_of(crtc, struct ingenic_drm, crtc); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline struct ingenic_drm *drm_nb_get_priv(struct notifier_block *nb) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci return container_of(nb, struct ingenic_drm, clock_nb); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int ingenic_drm_update_pixclk(struct notifier_block *nb, 1368c2ecf20Sopenharmony_ci unsigned long action, 1378c2ecf20Sopenharmony_ci void *data) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_nb_get_priv(nb); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch (action) { 1428c2ecf20Sopenharmony_ci case PRE_RATE_CHANGE: 1438c2ecf20Sopenharmony_ci mutex_lock(&priv->clk_mutex); 1448c2ecf20Sopenharmony_ci priv->update_clk_rate = true; 1458c2ecf20Sopenharmony_ci drm_crtc_wait_one_vblank(&priv->crtc); 1468c2ecf20Sopenharmony_ci return NOTIFY_OK; 1478c2ecf20Sopenharmony_ci default: 1488c2ecf20Sopenharmony_ci mutex_unlock(&priv->clk_mutex); 1498c2ecf20Sopenharmony_ci return NOTIFY_OK; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, 1548c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_STATE, 0); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 1618c2ecf20Sopenharmony_ci JZ_LCD_CTRL_ENABLE | JZ_LCD_CTRL_DISABLE, 1628c2ecf20Sopenharmony_ci JZ_LCD_CTRL_ENABLE); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void ingenic_drm_crtc_atomic_disable(struct drm_crtc *crtc, 1688c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 1718c2ecf20Sopenharmony_ci unsigned int var; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 1768c2ecf20Sopenharmony_ci JZ_LCD_CTRL_DISABLE, JZ_LCD_CTRL_DISABLE); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci regmap_read_poll_timeout(priv->map, JZ_REG_LCD_STATE, var, 1798c2ecf20Sopenharmony_ci var & JZ_LCD_STATE_DISABLED, 1808c2ecf20Sopenharmony_ci 1000, 0); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv, 1848c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci unsigned int vpe, vds, vde, vt, hpe, hds, hde, ht; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci vpe = mode->vsync_end - mode->vsync_start; 1898c2ecf20Sopenharmony_ci vds = mode->vtotal - mode->vsync_start; 1908c2ecf20Sopenharmony_ci vde = vds + mode->vdisplay; 1918c2ecf20Sopenharmony_ci vt = vde + mode->vsync_start - mode->vdisplay; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci hpe = mode->hsync_end - mode->hsync_start; 1948c2ecf20Sopenharmony_ci hds = mode->htotal - mode->hsync_start; 1958c2ecf20Sopenharmony_ci hde = hds + mode->hdisplay; 1968c2ecf20Sopenharmony_ci ht = hde + mode->hsync_start - mode->hdisplay; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_VSYNC, 1998c2ecf20Sopenharmony_ci 0 << JZ_LCD_VSYNC_VPS_OFFSET | 2008c2ecf20Sopenharmony_ci vpe << JZ_LCD_VSYNC_VPE_OFFSET); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_HSYNC, 2038c2ecf20Sopenharmony_ci 0 << JZ_LCD_HSYNC_HPS_OFFSET | 2048c2ecf20Sopenharmony_ci hpe << JZ_LCD_HSYNC_HPE_OFFSET); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_VAT, 2078c2ecf20Sopenharmony_ci ht << JZ_LCD_VAT_HT_OFFSET | 2088c2ecf20Sopenharmony_ci vt << JZ_LCD_VAT_VT_OFFSET); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_DAH, 2118c2ecf20Sopenharmony_ci hds << JZ_LCD_DAH_HDS_OFFSET | 2128c2ecf20Sopenharmony_ci hde << JZ_LCD_DAH_HDE_OFFSET); 2138c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_DAV, 2148c2ecf20Sopenharmony_ci vds << JZ_LCD_DAV_VDS_OFFSET | 2158c2ecf20Sopenharmony_ci vde << JZ_LCD_DAV_VDE_OFFSET); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (priv->panel_is_sharp) { 2188c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_PS, hde << 16 | (hde + 1)); 2198c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_CLS, hde << 16 | (hde + 1)); 2208c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1)); 2218c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci regmap_set_bits(priv->map, JZ_REG_LCD_CTRL, 2258c2ecf20Sopenharmony_ci JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * IPU restart - specify how much time the LCDC will wait before 2298c2ecf20Sopenharmony_ci * transferring a new frame from the IPU. The value is the one 2308c2ecf20Sopenharmony_ci * suggested in the programming manual. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN | 2338c2ecf20Sopenharmony_ci (ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, 2378c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 2408c2ecf20Sopenharmony_ci struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(state) && priv->soc_info->has_osd) { 2438c2ecf20Sopenharmony_ci f1_state = drm_atomic_get_plane_state(state->state, &priv->f1); 2448c2ecf20Sopenharmony_ci if (IS_ERR(f1_state)) 2458c2ecf20Sopenharmony_ci return PTR_ERR(f1_state); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci f0_state = drm_atomic_get_plane_state(state->state, &priv->f0); 2488c2ecf20Sopenharmony_ci if (IS_ERR(f0_state)) 2498c2ecf20Sopenharmony_ci return PTR_ERR(f0_state); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) { 2528c2ecf20Sopenharmony_ci ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane); 2538c2ecf20Sopenharmony_ci if (IS_ERR(ipu_state)) 2548c2ecf20Sopenharmony_ci return PTR_ERR(ipu_state); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* IPU and F1 planes cannot be enabled at the same time. */ 2578c2ecf20Sopenharmony_ci if (f1_state->fb && ipu_state->fb) { 2588c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n"); 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* If all the planes are disabled, we won't get a VBLANK IRQ */ 2648c2ecf20Sopenharmony_ci priv->no_vblank = !f1_state->fb && !f0_state->fb && 2658c2ecf20Sopenharmony_ci !(ipu_state && ipu_state->fb); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic enum drm_mode_status 2728c2ecf20Sopenharmony_ciingenic_drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 2758c2ecf20Sopenharmony_ci long rate; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (mode->hdisplay > priv->soc_info->max_width) 2788c2ecf20Sopenharmony_ci return MODE_BAD_HVALUE; 2798c2ecf20Sopenharmony_ci if (mode->vdisplay > priv->soc_info->max_height) 2808c2ecf20Sopenharmony_ci return MODE_BAD_VVALUE; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rate = clk_round_rate(priv->pix_clk, mode->clock * 1000); 2838c2ecf20Sopenharmony_ci if (rate < 0) 2848c2ecf20Sopenharmony_ci return MODE_CLOCK_RANGE; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return MODE_OK; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc, 2908c2ecf20Sopenharmony_ci struct drm_crtc_state *oldstate) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 2938c2ecf20Sopenharmony_ci u32 ctrl = 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd && 2968c2ecf20Sopenharmony_ci drm_atomic_crtc_needs_modeset(crtc->state)) { 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * If IPU plane is enabled, enable IPU as source for the F1 2998c2ecf20Sopenharmony_ci * plane; otherwise use regular DMA. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_ci if (priv->ipu_plane && priv->ipu_plane->state->fb) 3028c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_OSDCTRL_IPU; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, 3058c2ecf20Sopenharmony_ci JZ_LCD_OSDCTRL_IPU, ctrl); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, 3108c2ecf20Sopenharmony_ci struct drm_crtc_state *oldstate) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 3138c2ecf20Sopenharmony_ci struct drm_crtc_state *state = crtc->state; 3148c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event = state->event; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(state)) { 3178c2ecf20Sopenharmony_ci ingenic_drm_crtc_update_timings(priv, &state->mode); 3188c2ecf20Sopenharmony_ci priv->update_clk_rate = true; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (priv->update_clk_rate) { 3228c2ecf20Sopenharmony_ci mutex_lock(&priv->clk_mutex); 3238c2ecf20Sopenharmony_ci clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000); 3248c2ecf20Sopenharmony_ci priv->update_clk_rate = false; 3258c2ecf20Sopenharmony_ci mutex_unlock(&priv->clk_mutex); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (event) { 3298c2ecf20Sopenharmony_ci state->event = NULL; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 3328c2ecf20Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 3338c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 3368c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int ingenic_drm_plane_atomic_check(struct drm_plane *plane, 3418c2ecf20Sopenharmony_ci struct drm_plane_state *state) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 3448c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 3458c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!crtc) 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); 3528c2ecf20Sopenharmony_ci if (WARN_ON(!crtc_state)) 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check_plane_state(state, crtc_state, 3568c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 3578c2ecf20Sopenharmony_ci DRM_PLANE_HELPER_NO_SCALING, 3588c2ecf20Sopenharmony_ci priv->soc_info->has_osd, 3598c2ecf20Sopenharmony_ci true); 3608c2ecf20Sopenharmony_ci if (ret) 3618c2ecf20Sopenharmony_ci return ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * If OSD is not available, check that the width/height match. 3658c2ecf20Sopenharmony_ci * Note that state->src_* are in 16.16 fixed-point format. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_ci if (!priv->soc_info->has_osd && 3688c2ecf20Sopenharmony_ci (state->src_x != 0 || 3698c2ecf20Sopenharmony_ci (state->src_w >> 16) != state->crtc_w || 3708c2ecf20Sopenharmony_ci (state->src_h >> 16) != state->crtc_h)) 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * Require full modeset if enabling or disabling a plane, or changing 3758c2ecf20Sopenharmony_ci * its position, size or depth. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd && 3788c2ecf20Sopenharmony_ci (!plane->state->fb || !state->fb || 3798c2ecf20Sopenharmony_ci plane->state->crtc_x != state->crtc_x || 3808c2ecf20Sopenharmony_ci plane->state->crtc_y != state->crtc_y || 3818c2ecf20Sopenharmony_ci plane->state->crtc_w != state->crtc_w || 3828c2ecf20Sopenharmony_ci plane->state->crtc_h != state->crtc_h || 3838c2ecf20Sopenharmony_ci plane->state->fb->format->format != state->fb->format->format)) 3848c2ecf20Sopenharmony_ci crtc_state->mode_changed = true; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void ingenic_drm_plane_enable(struct ingenic_drm *priv, 3908c2ecf20Sopenharmony_ci struct drm_plane *plane) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci unsigned int en_bit; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd) { 3958c2ecf20Sopenharmony_ci if (plane != &priv->f0) 3968c2ecf20Sopenharmony_ci en_bit = JZ_LCD_OSDC_F1EN; 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci en_bit = JZ_LCD_OSDC_F0EN; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci regmap_set_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_civoid ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct ingenic_drm *priv = dev_get_drvdata(dev); 4078c2ecf20Sopenharmony_ci unsigned int en_bit; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd) { 4108c2ecf20Sopenharmony_ci if (plane != &priv->f0) 4118c2ecf20Sopenharmony_ci en_bit = JZ_LCD_OSDC_F1EN; 4128c2ecf20Sopenharmony_ci else 4138c2ecf20Sopenharmony_ci en_bit = JZ_LCD_OSDC_F0EN; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, 4208c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ingenic_drm_plane_disable(priv->dev, plane); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_civoid ingenic_drm_plane_config(struct device *dev, 4288c2ecf20Sopenharmony_ci struct drm_plane *plane, u32 fourcc) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct ingenic_drm *priv = dev_get_drvdata(dev); 4318c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 4328c2ecf20Sopenharmony_ci unsigned int xy_reg, size_reg; 4338c2ecf20Sopenharmony_ci unsigned int ctrl = 0; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ingenic_drm_plane_enable(priv, plane); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd && plane != &priv->f0) { 4388c2ecf20Sopenharmony_ci switch (fourcc) { 4398c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 4408c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_OSDCTRL_RGB555; 4418c2ecf20Sopenharmony_ci fallthrough; 4428c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 4438c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_OSDCTRL_BPP_15_16; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 4468c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_OSDCTRL_BPP_18_24; 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, 4518c2ecf20Sopenharmony_ci JZ_LCD_OSDCTRL_BPP_MASK, ctrl); 4528c2ecf20Sopenharmony_ci } else { 4538c2ecf20Sopenharmony_ci switch (fourcc) { 4548c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 4558c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_CTRL_RGB555; 4568c2ecf20Sopenharmony_ci fallthrough; 4578c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 4588c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_CTRL_BPP_15_16; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 4618c2ecf20Sopenharmony_ci ctrl |= JZ_LCD_CTRL_BPP_18_24; 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 4668c2ecf20Sopenharmony_ci JZ_LCD_CTRL_BPP_MASK, ctrl); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd) { 4708c2ecf20Sopenharmony_ci if (plane != &priv->f0) { 4718c2ecf20Sopenharmony_ci xy_reg = JZ_REG_LCD_XYP1; 4728c2ecf20Sopenharmony_ci size_reg = JZ_REG_LCD_SIZE1; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci xy_reg = JZ_REG_LCD_XYP0; 4758c2ecf20Sopenharmony_ci size_reg = JZ_REG_LCD_SIZE0; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci regmap_write(priv->map, xy_reg, 4798c2ecf20Sopenharmony_ci state->crtc_x << JZ_LCD_XYP01_XPOS_LSB | 4808c2ecf20Sopenharmony_ci state->crtc_y << JZ_LCD_XYP01_YPOS_LSB); 4818c2ecf20Sopenharmony_ci regmap_write(priv->map, size_reg, 4828c2ecf20Sopenharmony_ci state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB | 4838c2ecf20Sopenharmony_ci state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void ingenic_drm_plane_atomic_update(struct drm_plane *plane, 4888c2ecf20Sopenharmony_ci struct drm_plane_state *oldstate) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(plane->dev); 4918c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 4928c2ecf20Sopenharmony_ci struct ingenic_dma_hwdesc *hwdesc; 4938c2ecf20Sopenharmony_ci unsigned int width, height, cpp; 4948c2ecf20Sopenharmony_ci dma_addr_t addr; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (state && state->fb) { 4978c2ecf20Sopenharmony_ci addr = drm_fb_cma_get_gem_addr(state->fb, state, 0); 4988c2ecf20Sopenharmony_ci width = state->src_w >> 16; 4998c2ecf20Sopenharmony_ci height = state->src_h >> 16; 5008c2ecf20Sopenharmony_ci cpp = state->fb->format->cpp[0]; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (!priv->soc_info->has_osd || plane == &priv->f0) 5038c2ecf20Sopenharmony_ci hwdesc = priv->dma_hwdesc_f0; 5048c2ecf20Sopenharmony_ci else 5058c2ecf20Sopenharmony_ci hwdesc = priv->dma_hwdesc_f1; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci hwdesc->addr = addr; 5088c2ecf20Sopenharmony_ci hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (drm_atomic_crtc_needs_modeset(state->crtc->state)) 5118c2ecf20Sopenharmony_ci ingenic_drm_plane_config(priv->dev, plane, 5128c2ecf20Sopenharmony_ci state->fb->format->format); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder, 5178c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 5188c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(encoder->dev); 5218c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->adjusted_mode; 5228c2ecf20Sopenharmony_ci struct drm_connector *conn = conn_state->connector; 5238c2ecf20Sopenharmony_ci struct drm_display_info *info = &conn->display_info; 5248c2ecf20Sopenharmony_ci unsigned int cfg; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci priv->panel_is_sharp = info->bus_flags & DRM_BUS_FLAG_SHARP_SIGNALS; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (priv->panel_is_sharp) { 5298c2ecf20Sopenharmony_ci cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY; 5308c2ecf20Sopenharmony_ci } else { 5318c2ecf20Sopenharmony_ci cfg = JZ_LCD_CFG_PS_DISABLE | JZ_LCD_CFG_CLS_DISABLE 5328c2ecf20Sopenharmony_ci | JZ_LCD_CFG_SPL_DISABLE | JZ_LCD_CFG_REV_DISABLE; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 5368c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW; 5378c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 5388c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW; 5398c2ecf20Sopenharmony_ci if (info->bus_flags & DRM_BUS_FLAG_DE_LOW) 5408c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW; 5418c2ecf20Sopenharmony_ci if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) 5428c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!priv->panel_is_sharp) { 5458c2ecf20Sopenharmony_ci if (conn->connector_type == DRM_MODE_CONNECTOR_TV) { 5468c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 5478c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_TV_OUT_I; 5488c2ecf20Sopenharmony_ci else 5498c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_TV_OUT_P; 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci switch (*info->bus_formats) { 5528c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 5538c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT; 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 5568c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_GENERIC_18BIT; 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 5598c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_GENERIC_24BIT; 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_3X8: 5628c2ecf20Sopenharmony_ci cfg |= JZ_LCD_CFG_MODE_8BIT_SERIAL; 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci default: 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_CFG, cfg); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder, 5748c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 5758c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct drm_display_info *info = &conn_state->connector->display_info; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (info->num_bus_formats != 1) 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV) 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci switch (*info->bus_formats) { 5868c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 5878c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 5888c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 5898c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_3X8: 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci default: 5928c2ecf20Sopenharmony_ci return -EINVAL; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci /* 5998c2ecf20Sopenharmony_ci * Just your regular drm_atomic_helper_commit_tail(), but only calls 6008c2ecf20Sopenharmony_ci * drm_atomic_helper_wait_for_vblanks() if priv->no_vblank. 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci struct drm_device *dev = old_state->dev; 6038c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(dev); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci drm_atomic_helper_commit_modeset_disables(dev, old_state); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci drm_atomic_helper_commit_planes(dev, old_state, 0); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci drm_atomic_helper_commit_modeset_enables(dev, old_state); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci drm_atomic_helper_commit_hw_done(old_state); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (!priv->no_vblank) 6148c2ecf20Sopenharmony_ci drm_atomic_helper_wait_for_vblanks(dev, old_state); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci drm_atomic_helper_cleanup_planes(dev, old_state); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_device_get_priv(arg); 6228c2ecf20Sopenharmony_ci unsigned int state; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci regmap_read(priv->map, JZ_REG_LCD_STATE, &state); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_STATE, 6278c2ecf20Sopenharmony_ci JZ_LCD_STATE_EOF_IRQ, 0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (state & JZ_LCD_STATE_EOF_IRQ) 6308c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&priv->crtc); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int ingenic_drm_enable_vblank(struct drm_crtc *crtc) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, 6408c2ecf20Sopenharmony_ci JZ_LCD_CTRL_EOF_IRQ, JZ_LCD_CTRL_EOF_IRQ); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void ingenic_drm_disable_vblank(struct drm_crtc *crtc) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct ingenic_drm *priv = drm_crtc_get_priv(crtc); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_EOF_IRQ, 0); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(ingenic_drm_fops); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic struct drm_driver ingenic_drm_driver_data = { 6558c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 6568c2ecf20Sopenharmony_ci .name = "ingenic-drm", 6578c2ecf20Sopenharmony_ci .desc = "DRM module for Ingenic SoCs", 6588c2ecf20Sopenharmony_ci .date = "20200716", 6598c2ecf20Sopenharmony_ci .major = 1, 6608c2ecf20Sopenharmony_ci .minor = 1, 6618c2ecf20Sopenharmony_ci .patchlevel = 0, 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci .fops = &ingenic_drm_fops, 6648c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS, 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci .irq_handler = ingenic_drm_irq_handler, 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs ingenic_drm_primary_plane_funcs = { 6708c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 6718c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 6728c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 6738c2ecf20Sopenharmony_ci .destroy = drm_plane_cleanup, 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 6768c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs ingenic_drm_crtc_funcs = { 6808c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 6818c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 6828c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 6838c2ecf20Sopenharmony_ci .destroy = drm_crtc_cleanup, 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 6868c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci .enable_vblank = ingenic_drm_enable_vblank, 6898c2ecf20Sopenharmony_ci .disable_vblank = ingenic_drm_disable_vblank, 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci .gamma_set = drm_atomic_helper_legacy_gamma_set, 6928c2ecf20Sopenharmony_ci}; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { 6958c2ecf20Sopenharmony_ci .atomic_update = ingenic_drm_plane_atomic_update, 6968c2ecf20Sopenharmony_ci .atomic_check = ingenic_drm_plane_atomic_check, 6978c2ecf20Sopenharmony_ci .atomic_disable = ingenic_drm_plane_atomic_disable, 6988c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_fb_prepare_fb, 6998c2ecf20Sopenharmony_ci}; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = { 7028c2ecf20Sopenharmony_ci .atomic_enable = ingenic_drm_crtc_atomic_enable, 7038c2ecf20Sopenharmony_ci .atomic_disable = ingenic_drm_crtc_atomic_disable, 7048c2ecf20Sopenharmony_ci .atomic_begin = ingenic_drm_crtc_atomic_begin, 7058c2ecf20Sopenharmony_ci .atomic_flush = ingenic_drm_crtc_atomic_flush, 7068c2ecf20Sopenharmony_ci .atomic_check = ingenic_drm_crtc_atomic_check, 7078c2ecf20Sopenharmony_ci .mode_valid = ingenic_drm_crtc_mode_valid, 7088c2ecf20Sopenharmony_ci}; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs ingenic_drm_encoder_helper_funcs = { 7118c2ecf20Sopenharmony_ci .atomic_mode_set = ingenic_drm_encoder_atomic_mode_set, 7128c2ecf20Sopenharmony_ci .atomic_check = ingenic_drm_encoder_atomic_check, 7138c2ecf20Sopenharmony_ci}; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { 7168c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create, 7178c2ecf20Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed, 7188c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 7198c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 7208c2ecf20Sopenharmony_ci}; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { 7238c2ecf20Sopenharmony_ci .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail, 7248c2ecf20Sopenharmony_ci}; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic void ingenic_drm_unbind_all(void *d) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct ingenic_drm *priv = d; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci component_unbind_all(priv->dev, &priv->drm); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int ingenic_drm_bind(struct device *dev, bool has_components) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7368c2ecf20Sopenharmony_ci const struct jz_soc_info *soc_info; 7378c2ecf20Sopenharmony_ci struct ingenic_drm *priv; 7388c2ecf20Sopenharmony_ci struct clk *parent_clk; 7398c2ecf20Sopenharmony_ci struct drm_plane *primary; 7408c2ecf20Sopenharmony_ci struct drm_bridge *bridge; 7418c2ecf20Sopenharmony_ci struct drm_panel *panel; 7428c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 7438c2ecf20Sopenharmony_ci struct drm_device *drm; 7448c2ecf20Sopenharmony_ci void __iomem *base; 7458c2ecf20Sopenharmony_ci long parent_rate; 7468c2ecf20Sopenharmony_ci unsigned int i, clone_mask = 0; 7478c2ecf20Sopenharmony_ci int ret, irq; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci soc_info = of_device_get_match_data(dev); 7508c2ecf20Sopenharmony_ci if (!soc_info) { 7518c2ecf20Sopenharmony_ci dev_err(dev, "Missing platform data\n"); 7528c2ecf20Sopenharmony_ci return -EINVAL; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci priv = devm_drm_dev_alloc(dev, &ingenic_drm_driver_data, 7568c2ecf20Sopenharmony_ci struct ingenic_drm, drm); 7578c2ecf20Sopenharmony_ci if (IS_ERR(priv)) 7588c2ecf20Sopenharmony_ci return PTR_ERR(priv); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci priv->soc_info = soc_info; 7618c2ecf20Sopenharmony_ci priv->dev = dev; 7628c2ecf20Sopenharmony_ci drm = &priv->drm; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci ret = drmm_mode_config_init(drm); 7678c2ecf20Sopenharmony_ci if (ret) 7688c2ecf20Sopenharmony_ci return ret; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci drm->mode_config.min_width = 0; 7718c2ecf20Sopenharmony_ci drm->mode_config.min_height = 0; 7728c2ecf20Sopenharmony_ci drm->mode_config.max_width = soc_info->max_width; 7738c2ecf20Sopenharmony_ci drm->mode_config.max_height = 4095; 7748c2ecf20Sopenharmony_ci drm->mode_config.funcs = &ingenic_drm_mode_config_funcs; 7758c2ecf20Sopenharmony_ci drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 7788c2ecf20Sopenharmony_ci if (IS_ERR(base)) { 7798c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get memory resource\n"); 7808c2ecf20Sopenharmony_ci return PTR_ERR(base); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci priv->map = devm_regmap_init_mmio(dev, base, 7848c2ecf20Sopenharmony_ci &ingenic_drm_regmap_config); 7858c2ecf20Sopenharmony_ci if (IS_ERR(priv->map)) { 7868c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create regmap\n"); 7878c2ecf20Sopenharmony_ci return PTR_ERR(priv->map); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 7918c2ecf20Sopenharmony_ci if (irq < 0) 7928c2ecf20Sopenharmony_ci return irq; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (soc_info->needs_dev_clk) { 7958c2ecf20Sopenharmony_ci priv->lcd_clk = devm_clk_get(dev, "lcd"); 7968c2ecf20Sopenharmony_ci if (IS_ERR(priv->lcd_clk)) { 7978c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get lcd clock\n"); 7988c2ecf20Sopenharmony_ci return PTR_ERR(priv->lcd_clk); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci priv->pix_clk = devm_clk_get(dev, "lcd_pclk"); 8038c2ecf20Sopenharmony_ci if (IS_ERR(priv->pix_clk)) { 8048c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get pixel clock\n"); 8058c2ecf20Sopenharmony_ci return PTR_ERR(priv->pix_clk); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci priv->dma_hwdesc_f1 = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc_f1), 8098c2ecf20Sopenharmony_ci &priv->dma_hwdesc_phys_f1, 8108c2ecf20Sopenharmony_ci GFP_KERNEL); 8118c2ecf20Sopenharmony_ci if (!priv->dma_hwdesc_f1) 8128c2ecf20Sopenharmony_ci return -ENOMEM; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci priv->dma_hwdesc_f1->next = priv->dma_hwdesc_phys_f1; 8158c2ecf20Sopenharmony_ci priv->dma_hwdesc_f1->id = 0xf1; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (priv->soc_info->has_osd) { 8188c2ecf20Sopenharmony_ci priv->dma_hwdesc_f0 = dmam_alloc_coherent(dev, 8198c2ecf20Sopenharmony_ci sizeof(*priv->dma_hwdesc_f0), 8208c2ecf20Sopenharmony_ci &priv->dma_hwdesc_phys_f0, 8218c2ecf20Sopenharmony_ci GFP_KERNEL); 8228c2ecf20Sopenharmony_ci if (!priv->dma_hwdesc_f0) 8238c2ecf20Sopenharmony_ci return -ENOMEM; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci priv->dma_hwdesc_f0->next = priv->dma_hwdesc_phys_f0; 8268c2ecf20Sopenharmony_ci priv->dma_hwdesc_f0->id = 0xf0; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (soc_info->has_osd) 8308c2ecf20Sopenharmony_ci priv->ipu_plane = drm_plane_from_index(drm, 0); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci primary = priv->soc_info->has_osd ? &priv->f1 : &priv->f0; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci drm_plane_helper_add(primary, &ingenic_drm_plane_helper_funcs); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(drm, primary, 1, 8378c2ecf20Sopenharmony_ci &ingenic_drm_primary_plane_funcs, 8388c2ecf20Sopenharmony_ci ingenic_drm_primary_formats, 8398c2ecf20Sopenharmony_ci ARRAY_SIZE(ingenic_drm_primary_formats), 8408c2ecf20Sopenharmony_ci NULL, DRM_PLANE_TYPE_PRIMARY, NULL); 8418c2ecf20Sopenharmony_ci if (ret) { 8428c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register plane: %i\n", ret); 8438c2ecf20Sopenharmony_ci return ret; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &priv->crtc, primary, 8498c2ecf20Sopenharmony_ci NULL, &ingenic_drm_crtc_funcs, NULL); 8508c2ecf20Sopenharmony_ci if (ret) { 8518c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init CRTC: %i\n", ret); 8528c2ecf20Sopenharmony_ci return ret; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (soc_info->has_osd) { 8568c2ecf20Sopenharmony_ci drm_plane_helper_add(&priv->f0, 8578c2ecf20Sopenharmony_ci &ingenic_drm_plane_helper_funcs); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci ret = drm_universal_plane_init(drm, &priv->f0, 1, 8608c2ecf20Sopenharmony_ci &ingenic_drm_primary_plane_funcs, 8618c2ecf20Sopenharmony_ci ingenic_drm_primary_formats, 8628c2ecf20Sopenharmony_ci ARRAY_SIZE(ingenic_drm_primary_formats), 8638c2ecf20Sopenharmony_ci NULL, DRM_PLANE_TYPE_OVERLAY, 8648c2ecf20Sopenharmony_ci NULL); 8658c2ecf20Sopenharmony_ci if (ret) { 8668c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register overlay plane: %i\n", 8678c2ecf20Sopenharmony_ci ret); 8688c2ecf20Sopenharmony_ci return ret; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && has_components) { 8728c2ecf20Sopenharmony_ci ret = component_bind_all(dev, drm); 8738c2ecf20Sopenharmony_ci if (ret) { 8748c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 8758c2ecf20Sopenharmony_ci dev_err(dev, "Failed to bind components: %i\n", ret); 8768c2ecf20Sopenharmony_ci return ret; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv); 8808c2ecf20Sopenharmony_ci if (ret) 8818c2ecf20Sopenharmony_ci return ret; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci priv->ipu_plane = drm_plane_from_index(drm, 2); 8848c2ecf20Sopenharmony_ci if (!priv->ipu_plane) { 8858c2ecf20Sopenharmony_ci dev_err(dev, "Failed to retrieve IPU plane\n"); 8868c2ecf20Sopenharmony_ci return -EINVAL; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci for (i = 0; ; i++) { 8928c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i, &panel, &bridge); 8938c2ecf20Sopenharmony_ci if (ret) { 8948c2ecf20Sopenharmony_ci if (ret == -ENODEV) 8958c2ecf20Sopenharmony_ci break; /* we're done */ 8968c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 8978c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get bridge handle\n"); 8988c2ecf20Sopenharmony_ci return ret; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (panel) 9028c2ecf20Sopenharmony_ci bridge = devm_drm_panel_bridge_add_typed(dev, panel, 9038c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DPI); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); 9068c2ecf20Sopenharmony_ci if (!encoder) 9078c2ecf20Sopenharmony_ci return -ENOMEM; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci encoder->possible_crtcs = 1; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &ingenic_drm_encoder_helper_funcs); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DPI); 9148c2ecf20Sopenharmony_ci if (ret) { 9158c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init encoder: %d\n", ret); 9168c2ecf20Sopenharmony_ci return ret; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = drm_bridge_attach(encoder, bridge, NULL, 0); 9208c2ecf20Sopenharmony_ci if (ret) { 9218c2ecf20Sopenharmony_ci dev_err(dev, "Unable to attach bridge\n"); 9228c2ecf20Sopenharmony_ci return ret; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, drm) { 9278c2ecf20Sopenharmony_ci clone_mask |= BIT(drm_encoder_index(encoder)); 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, drm) { 9318c2ecf20Sopenharmony_ci encoder->possible_clones = clone_mask; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ret = drm_irq_install(drm, irq); 9358c2ecf20Sopenharmony_ci if (ret) { 9368c2ecf20Sopenharmony_ci dev_err(dev, "Unable to install IRQ handler\n"); 9378c2ecf20Sopenharmony_ci return ret; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm, 1); 9418c2ecf20Sopenharmony_ci if (ret) { 9428c2ecf20Sopenharmony_ci dev_err(dev, "Failed calling drm_vblank_init()\n"); 9438c2ecf20Sopenharmony_ci return ret; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->pix_clk); 9498c2ecf20Sopenharmony_ci if (ret) { 9508c2ecf20Sopenharmony_ci dev_err(dev, "Unable to start pixel clock\n"); 9518c2ecf20Sopenharmony_ci return ret; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (priv->lcd_clk) { 9558c2ecf20Sopenharmony_ci parent_clk = clk_get_parent(priv->lcd_clk); 9568c2ecf20Sopenharmony_ci parent_rate = clk_get_rate(parent_clk); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci /* LCD Device clock must be 3x the pixel clock for STN panels, 9598c2ecf20Sopenharmony_ci * or 1.5x the pixel clock for TFT panels. To avoid having to 9608c2ecf20Sopenharmony_ci * check for the LCD device clock everytime we do a mode change, 9618c2ecf20Sopenharmony_ci * we set the LCD device clock to the highest rate possible. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci ret = clk_set_rate(priv->lcd_clk, parent_rate); 9648c2ecf20Sopenharmony_ci if (ret) { 9658c2ecf20Sopenharmony_ci dev_err(dev, "Unable to set LCD clock rate\n"); 9668c2ecf20Sopenharmony_ci goto err_pixclk_disable; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->lcd_clk); 9708c2ecf20Sopenharmony_ci if (ret) { 9718c2ecf20Sopenharmony_ci dev_err(dev, "Unable to start lcd clock\n"); 9728c2ecf20Sopenharmony_ci goto err_pixclk_disable; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Set address of our DMA descriptor chain */ 9778c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys_f0); 9788c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_DA1, priv->dma_hwdesc_phys_f1); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Enable OSD if available */ 9818c2ecf20Sopenharmony_ci if (soc_info->has_osd) 9828c2ecf20Sopenharmony_ci regmap_write(priv->map, JZ_REG_LCD_OSDC, JZ_LCD_OSDC_OSDEN); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci mutex_init(&priv->clk_mutex); 9858c2ecf20Sopenharmony_ci priv->clock_nb.notifier_call = ingenic_drm_update_pixclk; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci parent_clk = clk_get_parent(priv->pix_clk); 9888c2ecf20Sopenharmony_ci ret = clk_notifier_register(parent_clk, &priv->clock_nb); 9898c2ecf20Sopenharmony_ci if (ret) { 9908c2ecf20Sopenharmony_ci dev_err(dev, "Unable to register clock notifier\n"); 9918c2ecf20Sopenharmony_ci goto err_devclk_disable; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 9958c2ecf20Sopenharmony_ci if (ret) { 9968c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register DRM driver\n"); 9978c2ecf20Sopenharmony_ci goto err_clk_notifier_unregister; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 32); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cierr_clk_notifier_unregister: 10058c2ecf20Sopenharmony_ci clk_notifier_unregister(parent_clk, &priv->clock_nb); 10068c2ecf20Sopenharmony_cierr_devclk_disable: 10078c2ecf20Sopenharmony_ci if (priv->lcd_clk) 10088c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->lcd_clk); 10098c2ecf20Sopenharmony_cierr_pixclk_disable: 10108c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->pix_clk); 10118c2ecf20Sopenharmony_ci return ret; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic int ingenic_drm_bind_with_components(struct device *dev) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci return ingenic_drm_bind(dev, true); 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic int compare_of(struct device *dev, void *data) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci return dev->of_node == data; 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cistatic void ingenic_drm_unbind(struct device *dev) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct ingenic_drm *priv = dev_get_drvdata(dev); 10278c2ecf20Sopenharmony_ci struct clk *parent_clk = clk_get_parent(priv->pix_clk); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci clk_notifier_unregister(parent_clk, &priv->clock_nb); 10308c2ecf20Sopenharmony_ci if (priv->lcd_clk) 10318c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->lcd_clk); 10328c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->pix_clk); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci drm_dev_unregister(&priv->drm); 10358c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(&priv->drm); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic const struct component_master_ops ingenic_master_ops = { 10398c2ecf20Sopenharmony_ci .bind = ingenic_drm_bind_with_components, 10408c2ecf20Sopenharmony_ci .unbind = ingenic_drm_unbind, 10418c2ecf20Sopenharmony_ci}; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int ingenic_drm_probe(struct platform_device *pdev) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 10468c2ecf20Sopenharmony_ci struct component_match *match = NULL; 10478c2ecf20Sopenharmony_ci struct device_node *np; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) 10508c2ecf20Sopenharmony_ci return ingenic_drm_bind(dev, false); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* IPU is at port address 8 */ 10538c2ecf20Sopenharmony_ci np = of_graph_get_remote_node(dev->of_node, 8, 0); 10548c2ecf20Sopenharmony_ci if (!np) 10558c2ecf20Sopenharmony_ci return ingenic_drm_bind(dev, false); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci drm_of_component_match_add(dev, &match, compare_of, np); 10588c2ecf20Sopenharmony_ci of_node_put(np); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return component_master_add_with_match(dev, &ingenic_master_ops, match); 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int ingenic_drm_remove(struct platform_device *pdev) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) 10688c2ecf20Sopenharmony_ci ingenic_drm_unbind(dev); 10698c2ecf20Sopenharmony_ci else 10708c2ecf20Sopenharmony_ci component_master_del(dev, &ingenic_master_ops); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic const struct jz_soc_info jz4740_soc_info = { 10768c2ecf20Sopenharmony_ci .needs_dev_clk = true, 10778c2ecf20Sopenharmony_ci .has_osd = false, 10788c2ecf20Sopenharmony_ci .max_width = 800, 10798c2ecf20Sopenharmony_ci .max_height = 600, 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic const struct jz_soc_info jz4725b_soc_info = { 10838c2ecf20Sopenharmony_ci .needs_dev_clk = false, 10848c2ecf20Sopenharmony_ci .has_osd = true, 10858c2ecf20Sopenharmony_ci .max_width = 800, 10868c2ecf20Sopenharmony_ci .max_height = 600, 10878c2ecf20Sopenharmony_ci}; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic const struct jz_soc_info jz4770_soc_info = { 10908c2ecf20Sopenharmony_ci .needs_dev_clk = false, 10918c2ecf20Sopenharmony_ci .has_osd = true, 10928c2ecf20Sopenharmony_ci .max_width = 1280, 10938c2ecf20Sopenharmony_ci .max_height = 720, 10948c2ecf20Sopenharmony_ci}; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic const struct of_device_id ingenic_drm_of_match[] = { 10978c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info }, 10988c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info }, 10998c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-lcd", .data = &jz4770_soc_info }, 11008c2ecf20Sopenharmony_ci { /* sentinel */ }, 11018c2ecf20Sopenharmony_ci}; 11028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ingenic_drm_of_match); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic struct platform_driver ingenic_drm_driver = { 11058c2ecf20Sopenharmony_ci .driver = { 11068c2ecf20Sopenharmony_ci .name = "ingenic-drm", 11078c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ingenic_drm_of_match), 11088c2ecf20Sopenharmony_ci }, 11098c2ecf20Sopenharmony_ci .probe = ingenic_drm_probe, 11108c2ecf20Sopenharmony_ci .remove = ingenic_drm_remove, 11118c2ecf20Sopenharmony_ci}; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int ingenic_drm_init(void) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci int err; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { 11188c2ecf20Sopenharmony_ci err = platform_driver_register(ingenic_ipu_driver_ptr); 11198c2ecf20Sopenharmony_ci if (err) 11208c2ecf20Sopenharmony_ci return err; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci err = platform_driver_register(&ingenic_drm_driver); 11248c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && err) 11258c2ecf20Sopenharmony_ci platform_driver_unregister(ingenic_ipu_driver_ptr); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci return err; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_cimodule_init(ingenic_drm_init); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic void ingenic_drm_exit(void) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci platform_driver_unregister(&ingenic_drm_driver); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) 11368c2ecf20Sopenharmony_ci platform_driver_unregister(ingenic_ipu_driver_ptr); 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_cimodule_exit(ingenic_drm_exit); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); 11418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n"); 11428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1143