18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Free Electrons 48c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_device.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/reset.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_framebuffer.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_plane.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "sun4i_drv.h" 248c2ecf20Sopenharmony_ci#include "sun4i_frontend.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const u32 sun4i_frontend_vert_coef[32] = { 278c2ecf20Sopenharmony_ci 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, 288c2ecf20Sopenharmony_ci 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, 298c2ecf20Sopenharmony_ci 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, 308c2ecf20Sopenharmony_ci 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, 318c2ecf20Sopenharmony_ci 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, 328c2ecf20Sopenharmony_ci 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, 338c2ecf20Sopenharmony_ci 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, 348c2ecf20Sopenharmony_ci 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const u32 sun4i_frontend_horz_coef[64] = { 388c2ecf20Sopenharmony_ci 0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03, 398c2ecf20Sopenharmony_ci 0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06, 408c2ecf20Sopenharmony_ci 0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09, 418c2ecf20Sopenharmony_ci 0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f, 428c2ecf20Sopenharmony_ci 0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12, 438c2ecf20Sopenharmony_ci 0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18, 448c2ecf20Sopenharmony_ci 0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e, 458c2ecf20Sopenharmony_ci 0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23, 468c2ecf20Sopenharmony_ci 0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29, 478c2ecf20Sopenharmony_ci 0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e, 488c2ecf20Sopenharmony_ci 0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33, 498c2ecf20Sopenharmony_ci 0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37, 508c2ecf20Sopenharmony_ci 0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b, 518c2ecf20Sopenharmony_ci 0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e, 528c2ecf20Sopenharmony_ci 0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40, 538c2ecf20Sopenharmony_ci 0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * These coefficients are taken from the A33 BSP from Allwinner. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * The first three values of each row are coded as 13-bit signed fixed-point 608c2ecf20Sopenharmony_ci * numbers, with 10 bits for the fractional part. The fourth value is a 618c2ecf20Sopenharmony_ci * constant coded as a 14-bit signed fixed-point number with 4 bits for the 628c2ecf20Sopenharmony_ci * fractional part. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * The values in table order give the following colorspace translation: 658c2ecf20Sopenharmony_ci * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135 668c2ecf20Sopenharmony_ci * R = 1.164 * Y + 1.596 * V - 222 678c2ecf20Sopenharmony_ci * B = 1.164 * Y + 2.018 * U + 276 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * This seems to be a conversion from Y[16:235] UV[16:240] to RGB[0:255], 708c2ecf20Sopenharmony_ci * following the BT601 spec. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ciconst u32 sunxi_bt601_yuv2rgb_coef[12] = { 738c2ecf20Sopenharmony_ci 0x000004a7, 0x00001e6f, 0x00001cbf, 0x00000877, 748c2ecf20Sopenharmony_ci 0x000004a7, 0x00000000, 0x00000662, 0x00003211, 758c2ecf20Sopenharmony_ci 0x000004a7, 0x00000812, 0x00000000, 0x00002eb1, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sunxi_bt601_yuv2rgb_coef); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (frontend->data->has_coef_access_ctrl) 848c2ecf20Sopenharmony_ci regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, 858c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL, 868c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 898c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i), 908c2ecf20Sopenharmony_ci sun4i_frontend_horz_coef[2 * i]); 918c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i), 928c2ecf20Sopenharmony_ci sun4i_frontend_horz_coef[2 * i]); 938c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i), 948c2ecf20Sopenharmony_ci sun4i_frontend_horz_coef[2 * i + 1]); 958c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i), 968c2ecf20Sopenharmony_ci sun4i_frontend_horz_coef[2 * i + 1]); 978c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i), 988c2ecf20Sopenharmony_ci sun4i_frontend_vert_coef[i]); 998c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i), 1008c2ecf20Sopenharmony_ci sun4i_frontend_vert_coef[i]); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (frontend->data->has_coef_rdy) 1048c2ecf20Sopenharmony_ci regmap_write_bits(frontend->regs, 1058c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_REG, 1068c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_COEF_RDY, 1078c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_COEF_RDY); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciint sun4i_frontend_init(struct sun4i_frontend *frontend) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return pm_runtime_get_sync(frontend->dev); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_init); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid sun4i_frontend_exit(struct sun4i_frontend *frontend) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci pm_runtime_put(frontend->dev); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_exit); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic bool sun4i_frontend_format_chroma_requires_swap(uint32_t fmt) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci switch (fmt) { 1258c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU411: 1268c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU420: 1278c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU422: 1288c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU444: 1298c2ecf20Sopenharmony_ci return true; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci default: 1328c2ecf20Sopenharmony_ci return false; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic bool sun4i_frontend_format_supports_tiling(uint32_t fmt) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci switch (fmt) { 1398c2ecf20Sopenharmony_ci case DRM_FORMAT_NV12: 1408c2ecf20Sopenharmony_ci case DRM_FORMAT_NV16: 1418c2ecf20Sopenharmony_ci case DRM_FORMAT_NV21: 1428c2ecf20Sopenharmony_ci case DRM_FORMAT_NV61: 1438c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV411: 1448c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV420: 1458c2ecf20Sopenharmony_ci case DRM_FORMAT_YUV422: 1468c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU420: 1478c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU422: 1488c2ecf20Sopenharmony_ci case DRM_FORMAT_YVU411: 1498c2ecf20Sopenharmony_ci return true; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci default: 1528c2ecf20Sopenharmony_ci return false; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_civoid sun4i_frontend_update_buffer(struct sun4i_frontend *frontend, 1578c2ecf20Sopenharmony_ci struct drm_plane *plane) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 1608c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 1618c2ecf20Sopenharmony_ci unsigned int strides[3] = {}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dma_addr_t paddr; 1648c2ecf20Sopenharmony_ci bool swap; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (fb->modifier == DRM_FORMAT_MOD_ALLWINNER_TILED) { 1678c2ecf20Sopenharmony_ci unsigned int width = state->src_w >> 16; 1688c2ecf20Sopenharmony_ci unsigned int offset; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci strides[0] = SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[0]); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * The X1 offset is the offset to the bottom-right point in the 1748c2ecf20Sopenharmony_ci * end tile, which is the final pixel (at offset width - 1) 1758c2ecf20Sopenharmony_ci * within the end tile (with a 32-byte mask). 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci offset = (width - 1) & (32 - 1); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF0_REG, 1808c2ecf20Sopenharmony_ci SUN4I_FRONTEND_TB_OFF_X1(offset)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (fb->format->num_planes > 1) { 1838c2ecf20Sopenharmony_ci strides[1] = 1848c2ecf20Sopenharmony_ci SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[1]); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF1_REG, 1878c2ecf20Sopenharmony_ci SUN4I_FRONTEND_TB_OFF_X1(offset)); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (fb->format->num_planes > 2) { 1918c2ecf20Sopenharmony_ci strides[2] = 1928c2ecf20Sopenharmony_ci SUN4I_FRONTEND_LINESTRD_TILED(fb->pitches[2]); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_TB_OFF2_REG, 1958c2ecf20Sopenharmony_ci SUN4I_FRONTEND_TB_OFF_X1(offset)); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci strides[0] = fb->pitches[0]; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (fb->format->num_planes > 1) 2018c2ecf20Sopenharmony_ci strides[1] = fb->pitches[1]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (fb->format->num_planes > 2) 2048c2ecf20Sopenharmony_ci strides[2] = fb->pitches[2]; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Set the line width */ 2088c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]); 2098c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG, 2108c2ecf20Sopenharmony_ci strides[0]); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (fb->format->num_planes > 1) 2138c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD1_REG, 2148c2ecf20Sopenharmony_ci strides[1]); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (fb->format->num_planes > 2) 2178c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD2_REG, 2188c2ecf20Sopenharmony_ci strides[2]); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Some planar formats require chroma channel swapping by hand. */ 2218c2ecf20Sopenharmony_ci swap = sun4i_frontend_format_chroma_requires_swap(fb->format->format); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Set the physical address of the buffer in memory */ 2248c2ecf20Sopenharmony_ci paddr = drm_fb_cma_get_gem_addr(fb, state, 0); 2258c2ecf20Sopenharmony_ci paddr -= PHYS_OFFSET; 2268c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Setting buffer #0 address to %pad\n", &paddr); 2278c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (fb->format->num_planes > 1) { 2308c2ecf20Sopenharmony_ci paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 2 : 1); 2318c2ecf20Sopenharmony_ci paddr -= PHYS_OFFSET; 2328c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Setting buffer #1 address to %pad\n", &paddr); 2338c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR1_REG, 2348c2ecf20Sopenharmony_ci paddr); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (fb->format->num_planes > 2) { 2388c2ecf20Sopenharmony_ci paddr = drm_fb_cma_get_gem_addr(fb, state, swap ? 1 : 2); 2398c2ecf20Sopenharmony_ci paddr -= PHYS_OFFSET; 2408c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Setting buffer #2 address to %pad\n", &paddr); 2418c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR2_REG, 2428c2ecf20Sopenharmony_ci paddr); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_update_buffer); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int 2488c2ecf20Sopenharmony_cisun4i_frontend_drm_format_to_input_fmt(const struct drm_format_info *format, 2498c2ecf20Sopenharmony_ci u32 *val) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci if (!format->is_yuv) 2528c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB; 2538c2ecf20Sopenharmony_ci else if (drm_format_info_is_yuv_sampling_411(format)) 2548c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV411; 2558c2ecf20Sopenharmony_ci else if (drm_format_info_is_yuv_sampling_420(format)) 2568c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV420; 2578c2ecf20Sopenharmony_ci else if (drm_format_info_is_yuv_sampling_422(format)) 2588c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV422; 2598c2ecf20Sopenharmony_ci else if (drm_format_info_is_yuv_sampling_444(format)) 2608c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_YUV444; 2618c2ecf20Sopenharmony_ci else 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int 2688c2ecf20Sopenharmony_cisun4i_frontend_drm_format_to_input_mode(const struct drm_format_info *format, 2698c2ecf20Sopenharmony_ci uint64_t modifier, u32 *val) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci bool tiled = (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci switch (format->num_planes) { 2748c2ecf20Sopenharmony_ci case 1: 2758c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED; 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci case 2: 2798c2ecf20Sopenharmony_ci *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_SEMIPLANAR 2808c2ecf20Sopenharmony_ci : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_SEMIPLANAR; 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci case 3: 2848c2ecf20Sopenharmony_ci *val = tiled ? SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_MB32_PLANAR 2858c2ecf20Sopenharmony_ci : SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PLANAR; 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci default: 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int 2948c2ecf20Sopenharmony_cisun4i_frontend_drm_format_to_input_sequence(const struct drm_format_info *format, 2958c2ecf20Sopenharmony_ci u32 *val) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci /* Planar formats have an explicit input sequence. */ 2988c2ecf20Sopenharmony_ci if (drm_format_info_is_yuv_planar(format)) { 2998c2ecf20Sopenharmony_ci *val = 0; 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci switch (format->format) { 3048c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX8888: 3058c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX; 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci case DRM_FORMAT_NV12: 3098c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV; 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci case DRM_FORMAT_NV16: 3138c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UV; 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci case DRM_FORMAT_NV21: 3178c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU; 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci case DRM_FORMAT_NV61: 3218c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VU; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci case DRM_FORMAT_UYVY: 3258c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_UYVY; 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci case DRM_FORMAT_VYUY: 3298c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_VYUY; 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 3338c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB; 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci case DRM_FORMAT_YUYV: 3378c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YUYV; 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci case DRM_FORMAT_YVYU: 3418c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_YVYU; 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci return -EINVAL; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci switch (fmt) { 3528c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX8888: 3538c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888; 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 3578c2ecf20Sopenharmony_ci *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888; 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci default: 3618c2ecf20Sopenharmony_ci return -EINVAL; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic const uint32_t sun4i_frontend_formats[] = { 3668c2ecf20Sopenharmony_ci DRM_FORMAT_BGRX8888, 3678c2ecf20Sopenharmony_ci DRM_FORMAT_NV12, 3688c2ecf20Sopenharmony_ci DRM_FORMAT_NV16, 3698c2ecf20Sopenharmony_ci DRM_FORMAT_NV21, 3708c2ecf20Sopenharmony_ci DRM_FORMAT_NV61, 3718c2ecf20Sopenharmony_ci DRM_FORMAT_UYVY, 3728c2ecf20Sopenharmony_ci DRM_FORMAT_VYUY, 3738c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 3748c2ecf20Sopenharmony_ci DRM_FORMAT_YUV411, 3758c2ecf20Sopenharmony_ci DRM_FORMAT_YUV420, 3768c2ecf20Sopenharmony_ci DRM_FORMAT_YUV422, 3778c2ecf20Sopenharmony_ci DRM_FORMAT_YUV444, 3788c2ecf20Sopenharmony_ci DRM_FORMAT_YUYV, 3798c2ecf20Sopenharmony_ci DRM_FORMAT_YVU411, 3808c2ecf20Sopenharmony_ci DRM_FORMAT_YVU420, 3818c2ecf20Sopenharmony_ci DRM_FORMAT_YVU422, 3828c2ecf20Sopenharmony_ci DRM_FORMAT_YVU444, 3838c2ecf20Sopenharmony_ci DRM_FORMAT_YVYU, 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cibool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci unsigned int i; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (modifier == DRM_FORMAT_MOD_ALLWINNER_TILED) 3918c2ecf20Sopenharmony_ci return sun4i_frontend_format_supports_tiling(fmt); 3928c2ecf20Sopenharmony_ci else if (modifier != DRM_FORMAT_MOD_LINEAR) 3938c2ecf20Sopenharmony_ci return false; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++) 3968c2ecf20Sopenharmony_ci if (sun4i_frontend_formats[i] == fmt) 3978c2ecf20Sopenharmony_ci return true; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return false; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_format_is_supported); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciint sun4i_frontend_update_formats(struct sun4i_frontend *frontend, 4048c2ecf20Sopenharmony_ci struct drm_plane *plane, uint32_t out_fmt) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 4078c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 4088c2ecf20Sopenharmony_ci const struct drm_format_info *format = fb->format; 4098c2ecf20Sopenharmony_ci uint64_t modifier = fb->modifier; 4108c2ecf20Sopenharmony_ci unsigned int ch1_phase_idx; 4118c2ecf20Sopenharmony_ci u32 out_fmt_val; 4128c2ecf20Sopenharmony_ci u32 in_fmt_val, in_mod_val, in_ps_val; 4138c2ecf20Sopenharmony_ci unsigned int i; 4148c2ecf20Sopenharmony_ci u32 bypass; 4158c2ecf20Sopenharmony_ci int ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val); 4188c2ecf20Sopenharmony_ci if (ret) { 4198c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Invalid input format\n"); 4208c2ecf20Sopenharmony_ci return ret; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = sun4i_frontend_drm_format_to_input_mode(format, modifier, 4248c2ecf20Sopenharmony_ci &in_mod_val); 4258c2ecf20Sopenharmony_ci if (ret) { 4268c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Invalid input mode\n"); 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ret = sun4i_frontend_drm_format_to_input_sequence(format, &in_ps_val); 4318c2ecf20Sopenharmony_ci if (ret) { 4328c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Invalid pixel sequence\n"); 4338c2ecf20Sopenharmony_ci return ret; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val); 4378c2ecf20Sopenharmony_ci if (ret) { 4388c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Invalid output format\n"); 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * I have no idea what this does exactly, but it seems to be 4448c2ecf20Sopenharmony_ci * related to the scaler FIR filter phase parameters. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci ch1_phase_idx = (format->num_planes > 1) ? 1 : 0; 4478c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 4488c2ecf20Sopenharmony_ci frontend->data->ch_phase[0]); 4498c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 4508c2ecf20Sopenharmony_ci frontend->data->ch_phase[ch1_phase_idx]); 4518c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 4528c2ecf20Sopenharmony_ci frontend->data->ch_phase[0]); 4538c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 4548c2ecf20Sopenharmony_ci frontend->data->ch_phase[ch1_phase_idx]); 4558c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 4568c2ecf20Sopenharmony_ci frontend->data->ch_phase[0]); 4578c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 4588c2ecf20Sopenharmony_ci frontend->data->ch_phase[ch1_phase_idx]); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* 4618c2ecf20Sopenharmony_ci * Checking the input format is sufficient since we currently only 4628c2ecf20Sopenharmony_ci * support RGB output formats to the backend. If YUV output formats 4638c2ecf20Sopenharmony_ci * ever get supported, an YUV input and output would require bypassing 4648c2ecf20Sopenharmony_ci * the CSC engine too. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci if (format->is_yuv) { 4678c2ecf20Sopenharmony_ci /* Setup the CSC engine for YUV to RGB conversion. */ 4688c2ecf20Sopenharmony_ci bypass = 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sunxi_bt601_yuv2rgb_coef); i++) 4718c2ecf20Sopenharmony_ci regmap_write(frontend->regs, 4728c2ecf20Sopenharmony_ci SUN4I_FRONTEND_CSC_COEF_REG(i), 4738c2ecf20Sopenharmony_ci sunxi_bt601_yuv2rgb_coef[i]); 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci bypass = SUN4I_FRONTEND_BYPASS_CSC_EN; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG, 4798c2ecf20Sopenharmony_ci SUN4I_FRONTEND_BYPASS_CSC_EN, bypass); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG, 4828c2ecf20Sopenharmony_ci in_mod_val | in_fmt_val | in_ps_val); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * TODO: It look like the A31 and A80 at least will need the 4868c2ecf20Sopenharmony_ci * bit 7 (ALPHA_EN) enabled when using a format with alpha (so 4878c2ecf20Sopenharmony_ci * ARGB8888). 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG, 4908c2ecf20Sopenharmony_ci out_fmt_val); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_update_formats); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid sun4i_frontend_update_coord(struct sun4i_frontend *frontend, 4978c2ecf20Sopenharmony_ci struct drm_plane *plane) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 5008c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->fb; 5018c2ecf20Sopenharmony_ci uint32_t luma_width, luma_height; 5028c2ecf20Sopenharmony_ci uint32_t chroma_width, chroma_height; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Set height and width */ 5058c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n", 5068c2ecf20Sopenharmony_ci state->crtc_w, state->crtc_h); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci luma_width = state->src_w >> 16; 5098c2ecf20Sopenharmony_ci luma_height = state->src_h >> 16; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci chroma_width = DIV_ROUND_UP(luma_width, fb->format->hsub); 5128c2ecf20Sopenharmony_ci chroma_height = DIV_ROUND_UP(luma_height, fb->format->vsub); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG, 5158c2ecf20Sopenharmony_ci SUN4I_FRONTEND_INSIZE(luma_height, luma_width)); 5168c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG, 5178c2ecf20Sopenharmony_ci SUN4I_FRONTEND_INSIZE(chroma_height, chroma_width)); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG, 5208c2ecf20Sopenharmony_ci SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); 5218c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG, 5228c2ecf20Sopenharmony_ci SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG, 5258c2ecf20Sopenharmony_ci (luma_width << 16) / state->crtc_w); 5268c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG, 5278c2ecf20Sopenharmony_ci (chroma_width << 16) / state->crtc_w); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG, 5308c2ecf20Sopenharmony_ci (luma_height << 16) / state->crtc_h); 5318c2ecf20Sopenharmony_ci regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG, 5328c2ecf20Sopenharmony_ci (chroma_height << 16) / state->crtc_h); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, 5358c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_REG_RDY, 5368c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_REG_RDY); 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_update_coord); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ciint sun4i_frontend_enable(struct sun4i_frontend *frontend) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, 5438c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_FRM_START, 5448c2ecf20Sopenharmony_ci SUN4I_FRONTEND_FRM_CTRL_FRM_START); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_enable); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct regmap_config sun4i_frontend_regmap_config = { 5518c2ecf20Sopenharmony_ci .reg_bits = 32, 5528c2ecf20Sopenharmony_ci .val_bits = 32, 5538c2ecf20Sopenharmony_ci .reg_stride = 4, 5548c2ecf20Sopenharmony_ci .max_register = 0x0a14, 5558c2ecf20Sopenharmony_ci}; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int sun4i_frontend_bind(struct device *dev, struct device *master, 5588c2ecf20Sopenharmony_ci void *data) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5618c2ecf20Sopenharmony_ci struct sun4i_frontend *frontend; 5628c2ecf20Sopenharmony_ci struct drm_device *drm = data; 5638c2ecf20Sopenharmony_ci struct sun4i_drv *drv = drm->dev_private; 5648c2ecf20Sopenharmony_ci struct resource *res; 5658c2ecf20Sopenharmony_ci void __iomem *regs; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL); 5688c2ecf20Sopenharmony_ci if (!frontend) 5698c2ecf20Sopenharmony_ci return -ENOMEM; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci dev_set_drvdata(dev, frontend); 5728c2ecf20Sopenharmony_ci frontend->dev = dev; 5738c2ecf20Sopenharmony_ci frontend->node = dev->of_node; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci frontend->data = of_device_get_match_data(dev); 5768c2ecf20Sopenharmony_ci if (!frontend->data) 5778c2ecf20Sopenharmony_ci return -ENODEV; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5808c2ecf20Sopenharmony_ci regs = devm_ioremap_resource(dev, res); 5818c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 5828c2ecf20Sopenharmony_ci return PTR_ERR(regs); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci frontend->regs = devm_regmap_init_mmio(dev, regs, 5858c2ecf20Sopenharmony_ci &sun4i_frontend_regmap_config); 5868c2ecf20Sopenharmony_ci if (IS_ERR(frontend->regs)) { 5878c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't create the frontend regmap\n"); 5888c2ecf20Sopenharmony_ci return PTR_ERR(frontend->regs); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci frontend->reset = devm_reset_control_get(dev, NULL); 5928c2ecf20Sopenharmony_ci if (IS_ERR(frontend->reset)) { 5938c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't get our reset line\n"); 5948c2ecf20Sopenharmony_ci return PTR_ERR(frontend->reset); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci frontend->bus_clk = devm_clk_get(dev, "ahb"); 5988c2ecf20Sopenharmony_ci if (IS_ERR(frontend->bus_clk)) { 5998c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't get our bus clock\n"); 6008c2ecf20Sopenharmony_ci return PTR_ERR(frontend->bus_clk); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci frontend->mod_clk = devm_clk_get(dev, "mod"); 6048c2ecf20Sopenharmony_ci if (IS_ERR(frontend->mod_clk)) { 6058c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't get our mod clock\n"); 6068c2ecf20Sopenharmony_ci return PTR_ERR(frontend->mod_clk); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci frontend->ram_clk = devm_clk_get(dev, "ram"); 6108c2ecf20Sopenharmony_ci if (IS_ERR(frontend->ram_clk)) { 6118c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't get our ram clock\n"); 6128c2ecf20Sopenharmony_ci return PTR_ERR(frontend->ram_clk); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci list_add_tail(&frontend->list, &drv->frontend_list); 6168c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic void sun4i_frontend_unbind(struct device *dev, struct device *master, 6228c2ecf20Sopenharmony_ci void *data) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct sun4i_frontend *frontend = dev_get_drvdata(dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci list_del(&frontend->list); 6278c2ecf20Sopenharmony_ci pm_runtime_force_suspend(dev); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic const struct component_ops sun4i_frontend_ops = { 6318c2ecf20Sopenharmony_ci .bind = sun4i_frontend_bind, 6328c2ecf20Sopenharmony_ci .unbind = sun4i_frontend_unbind, 6338c2ecf20Sopenharmony_ci}; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int sun4i_frontend_probe(struct platform_device *pdev) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &sun4i_frontend_ops); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int sun4i_frontend_remove(struct platform_device *pdev) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci component_del(&pdev->dev, &sun4i_frontend_ops); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int sun4i_frontend_runtime_resume(struct device *dev) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct sun4i_frontend *frontend = dev_get_drvdata(dev); 6508c2ecf20Sopenharmony_ci int ret; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci clk_set_rate(frontend->mod_clk, 300000000); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci clk_prepare_enable(frontend->bus_clk); 6558c2ecf20Sopenharmony_ci clk_prepare_enable(frontend->mod_clk); 6568c2ecf20Sopenharmony_ci clk_prepare_enable(frontend->ram_clk); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ret = reset_control_reset(frontend->reset); 6598c2ecf20Sopenharmony_ci if (ret) { 6608c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't reset our device\n"); 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG, 6658c2ecf20Sopenharmony_ci SUN4I_FRONTEND_EN_EN, 6668c2ecf20Sopenharmony_ci SUN4I_FRONTEND_EN_EN); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci sun4i_frontend_scaler_init(frontend); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic int sun4i_frontend_runtime_suspend(struct device *dev) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct sun4i_frontend *frontend = dev_get_drvdata(dev); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci clk_disable_unprepare(frontend->ram_clk); 6788c2ecf20Sopenharmony_ci clk_disable_unprepare(frontend->mod_clk); 6798c2ecf20Sopenharmony_ci clk_disable_unprepare(frontend->bus_clk); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci reset_control_assert(frontend->reset); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sun4i_frontend_pm_ops = { 6878c2ecf20Sopenharmony_ci .runtime_resume = sun4i_frontend_runtime_resume, 6888c2ecf20Sopenharmony_ci .runtime_suspend = sun4i_frontend_runtime_suspend, 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic const struct sun4i_frontend_data sun4i_a10_frontend = { 6928c2ecf20Sopenharmony_ci .ch_phase = { 0x000, 0xfc000 }, 6938c2ecf20Sopenharmony_ci .has_coef_rdy = true, 6948c2ecf20Sopenharmony_ci}; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic const struct sun4i_frontend_data sun8i_a33_frontend = { 6978c2ecf20Sopenharmony_ci .ch_phase = { 0x400, 0xfc400 }, 6988c2ecf20Sopenharmony_ci .has_coef_access_ctrl = true, 6998c2ecf20Sopenharmony_ci}; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ciconst struct of_device_id sun4i_frontend_of_table[] = { 7028c2ecf20Sopenharmony_ci { 7038c2ecf20Sopenharmony_ci .compatible = "allwinner,sun4i-a10-display-frontend", 7048c2ecf20Sopenharmony_ci .data = &sun4i_a10_frontend 7058c2ecf20Sopenharmony_ci }, 7068c2ecf20Sopenharmony_ci { 7078c2ecf20Sopenharmony_ci .compatible = "allwinner,sun7i-a20-display-frontend", 7088c2ecf20Sopenharmony_ci .data = &sun4i_a10_frontend 7098c2ecf20Sopenharmony_ci }, 7108c2ecf20Sopenharmony_ci { 7118c2ecf20Sopenharmony_ci .compatible = "allwinner,sun8i-a23-display-frontend", 7128c2ecf20Sopenharmony_ci .data = &sun8i_a33_frontend 7138c2ecf20Sopenharmony_ci }, 7148c2ecf20Sopenharmony_ci { 7158c2ecf20Sopenharmony_ci .compatible = "allwinner,sun8i-a33-display-frontend", 7168c2ecf20Sopenharmony_ci .data = &sun8i_a33_frontend 7178c2ecf20Sopenharmony_ci }, 7188c2ecf20Sopenharmony_ci { } 7198c2ecf20Sopenharmony_ci}; 7208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sun4i_frontend_of_table); 7218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_frontend_of_table); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic struct platform_driver sun4i_frontend_driver = { 7248c2ecf20Sopenharmony_ci .probe = sun4i_frontend_probe, 7258c2ecf20Sopenharmony_ci .remove = sun4i_frontend_remove, 7268c2ecf20Sopenharmony_ci .driver = { 7278c2ecf20Sopenharmony_ci .name = "sun4i-frontend", 7288c2ecf20Sopenharmony_ci .of_match_table = sun4i_frontend_of_table, 7298c2ecf20Sopenharmony_ci .pm = &sun4i_frontend_pm_ops, 7308c2ecf20Sopenharmony_ci }, 7318c2ecf20Sopenharmony_ci}; 7328c2ecf20Sopenharmony_cimodule_platform_driver(sun4i_frontend_driver); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 7358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver"); 7368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 737