18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/export.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/types.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <video/imx-ipu-v3.h> 158c2ecf20Sopenharmony_ci#include "ipu-prv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct ipu_di { 188c2ecf20Sopenharmony_ci void __iomem *base; 198c2ecf20Sopenharmony_ci int id; 208c2ecf20Sopenharmony_ci u32 module; 218c2ecf20Sopenharmony_ci struct clk *clk_di; /* display input clock */ 228c2ecf20Sopenharmony_ci struct clk *clk_ipu; /* IPU bus clock */ 238c2ecf20Sopenharmony_ci struct clk *clk_di_pixel; /* resulting pixel clock */ 248c2ecf20Sopenharmony_ci bool inuse; 258c2ecf20Sopenharmony_ci struct ipu_soc *ipu; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(di_mutex); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct di_sync_config { 318c2ecf20Sopenharmony_ci int run_count; 328c2ecf20Sopenharmony_ci int run_src; 338c2ecf20Sopenharmony_ci int offset_count; 348c2ecf20Sopenharmony_ci int offset_src; 358c2ecf20Sopenharmony_ci int repeat_count; 368c2ecf20Sopenharmony_ci int cnt_clr_src; 378c2ecf20Sopenharmony_ci int cnt_polarity_gen_en; 388c2ecf20Sopenharmony_ci int cnt_polarity_clr_src; 398c2ecf20Sopenharmony_ci int cnt_polarity_trigger_src; 408c2ecf20Sopenharmony_ci int cnt_up; 418c2ecf20Sopenharmony_ci int cnt_down; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cienum di_pins { 458c2ecf20Sopenharmony_ci DI_PIN11 = 0, 468c2ecf20Sopenharmony_ci DI_PIN12 = 1, 478c2ecf20Sopenharmony_ci DI_PIN13 = 2, 488c2ecf20Sopenharmony_ci DI_PIN14 = 3, 498c2ecf20Sopenharmony_ci DI_PIN15 = 4, 508c2ecf20Sopenharmony_ci DI_PIN16 = 5, 518c2ecf20Sopenharmony_ci DI_PIN17 = 6, 528c2ecf20Sopenharmony_ci DI_PIN_CS = 7, 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci DI_PIN_SER_CLK = 0, 558c2ecf20Sopenharmony_ci DI_PIN_SER_RS = 1, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cienum di_sync_wave { 598c2ecf20Sopenharmony_ci DI_SYNC_NONE = 0, 608c2ecf20Sopenharmony_ci DI_SYNC_CLK = 1, 618c2ecf20Sopenharmony_ci DI_SYNC_INT_HSYNC = 2, 628c2ecf20Sopenharmony_ci DI_SYNC_HSYNC = 3, 638c2ecf20Sopenharmony_ci DI_SYNC_VSYNC = 4, 648c2ecf20Sopenharmony_ci DI_SYNC_DE = 6, 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci DI_SYNC_CNT1 = 2, /* counter >= 2 only */ 678c2ecf20Sopenharmony_ci DI_SYNC_CNT4 = 5, /* counter >= 5 only */ 688c2ecf20Sopenharmony_ci DI_SYNC_CNT5 = 6, /* counter >= 6 only */ 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define SYNC_WAVE 0 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define DI_GENERAL 0x0000 748c2ecf20Sopenharmony_ci#define DI_BS_CLKGEN0 0x0004 758c2ecf20Sopenharmony_ci#define DI_BS_CLKGEN1 0x0008 768c2ecf20Sopenharmony_ci#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) 778c2ecf20Sopenharmony_ci#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) 788c2ecf20Sopenharmony_ci#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) 798c2ecf20Sopenharmony_ci#define DI_SYNC_AS_GEN 0x0054 808c2ecf20Sopenharmony_ci#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) 818c2ecf20Sopenharmony_ci#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) 828c2ecf20Sopenharmony_ci#define DI_SER_CONF 0x015c 838c2ecf20Sopenharmony_ci#define DI_SSC 0x0160 848c2ecf20Sopenharmony_ci#define DI_POL 0x0164 858c2ecf20Sopenharmony_ci#define DI_AW0 0x0168 868c2ecf20Sopenharmony_ci#define DI_AW1 0x016c 878c2ecf20Sopenharmony_ci#define DI_SCR_CONF 0x0170 888c2ecf20Sopenharmony_ci#define DI_STAT 0x0174 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) 918c2ecf20Sopenharmony_ci#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) 928c2ecf20Sopenharmony_ci#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) 938c2ecf20Sopenharmony_ci#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) 968c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) 978c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) 988c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) 998c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) 1008c2ecf20Sopenharmony_ci#define DI_SW_GEN1_CNT_UP(x) (x) 1018c2ecf20Sopenharmony_ci#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 1048c2ecf20Sopenharmony_ci#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_1 (1 << 0) 1078c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_2 (1 << 1) 1088c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_3 (1 << 2) 1098c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_4 (1 << 3) 1108c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_5 (1 << 4) 1118c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_6 (1 << 5) 1128c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_7 (1 << 6) 1138c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_8 (1 << 7) 1148c2ecf20Sopenharmony_ci#define DI_GEN_POLARITY_DISP_CLK (1 << 17) 1158c2ecf20Sopenharmony_ci#define DI_GEN_DI_CLK_EXT (1 << 20) 1168c2ecf20Sopenharmony_ci#define DI_GEN_DI_VSYNC_EXT (1 << 21) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define DI_POL_DRDY_DATA_POLARITY (1 << 7) 1198c2ecf20Sopenharmony_ci#define DI_POL_DRDY_POLARITY_15 (1 << 4) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define DI_VSYNC_SEL_OFFSET 13 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return readl(di->base + offset); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci writel(value, di->base + offset); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void ipu_di_data_wave_config(struct ipu_di *di, 1348c2ecf20Sopenharmony_ci int wave_gen, 1358c2ecf20Sopenharmony_ci int access_size, int component_size) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci u32 reg; 1388c2ecf20Sopenharmony_ci reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | 1398c2ecf20Sopenharmony_ci (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); 1408c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, 1448c2ecf20Sopenharmony_ci int set, int up, int down) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci u32 reg; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); 1498c2ecf20Sopenharmony_ci reg &= ~(0x3 << (di_pin * 2)); 1508c2ecf20Sopenharmony_ci reg |= set << (di_pin * 2); 1518c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, 1578c2ecf20Sopenharmony_ci int start, int count) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci u32 reg; 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1638c2ecf20Sopenharmony_ci struct di_sync_config *c = &config[i]; 1648c2ecf20Sopenharmony_ci int wave_gen = start + i + 1; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || 1678c2ecf20Sopenharmony_ci (c->repeat_count >= 0x1000) || 1688c2ecf20Sopenharmony_ci (c->cnt_up >= 0x400) || 1698c2ecf20Sopenharmony_ci (c->cnt_down >= 0x400)) { 1708c2ecf20Sopenharmony_ci dev_err(di->ipu->dev, "DI%d counters out of range.\n", 1718c2ecf20Sopenharmony_ci di->id); 1728c2ecf20Sopenharmony_ci return; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | 1768c2ecf20Sopenharmony_ci DI_SW_GEN0_RUN_SRC(c->run_src) | 1778c2ecf20Sopenharmony_ci DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | 1788c2ecf20Sopenharmony_ci DI_SW_GEN0_OFFSET_SRC(c->offset_src); 1798c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | 1828c2ecf20Sopenharmony_ci DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | 1838c2ecf20Sopenharmony_ci DI_SW_GEN1_CNT_POL_TRIGGER_SRC( 1848c2ecf20Sopenharmony_ci c->cnt_polarity_trigger_src) | 1858c2ecf20Sopenharmony_ci DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | 1868c2ecf20Sopenharmony_ci DI_SW_GEN1_CNT_DOWN(c->cnt_down) | 1878c2ecf20Sopenharmony_ci DI_SW_GEN1_CNT_UP(c->cnt_up); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Enable auto reload */ 1908c2ecf20Sopenharmony_ci if (c->repeat_count == 0) 1918c2ecf20Sopenharmony_ci reg |= DI_SW_GEN1_AUTO_RELOAD; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci reg = ipu_di_read(di, DI_STP_REP(wave_gen)); 1968c2ecf20Sopenharmony_ci reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); 1978c2ecf20Sopenharmony_ci reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); 1988c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_STP_REP(wave_gen)); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void ipu_di_sync_config_interlaced(struct ipu_di *di, 2038c2ecf20Sopenharmony_ci struct ipu_di_signal_cfg *sig) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci u32 h_total = sig->mode.hactive + sig->mode.hsync_len + 2068c2ecf20Sopenharmony_ci sig->mode.hback_porch + sig->mode.hfront_porch; 2078c2ecf20Sopenharmony_ci u32 v_total = sig->mode.vactive + sig->mode.vsync_len + 2088c2ecf20Sopenharmony_ci sig->mode.vback_porch + sig->mode.vfront_porch; 2098c2ecf20Sopenharmony_ci struct di_sync_config cfg[] = { 2108c2ecf20Sopenharmony_ci { 2118c2ecf20Sopenharmony_ci /* 1: internal VSYNC for each frame */ 2128c2ecf20Sopenharmony_ci .run_count = v_total * 2 - 1, 2138c2ecf20Sopenharmony_ci .run_src = 3, /* == counter 7 */ 2148c2ecf20Sopenharmony_ci }, { 2158c2ecf20Sopenharmony_ci /* PIN2: HSYNC waveform */ 2168c2ecf20Sopenharmony_ci .run_count = h_total - 1, 2178c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 2188c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 2198c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 2208c2ecf20Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 2218c2ecf20Sopenharmony_ci }, { 2228c2ecf20Sopenharmony_ci /* PIN3: VSYNC waveform */ 2238c2ecf20Sopenharmony_ci .run_count = v_total - 1, 2248c2ecf20Sopenharmony_ci .run_src = 4, /* == counter 7 */ 2258c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 2268c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = 4, /* == counter 7 */ 2278c2ecf20Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 2288c2ecf20Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT1, 2298c2ecf20Sopenharmony_ci }, { 2308c2ecf20Sopenharmony_ci /* 4: Field */ 2318c2ecf20Sopenharmony_ci .run_count = v_total / 2, 2328c2ecf20Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 2338c2ecf20Sopenharmony_ci .offset_count = h_total / 2, 2348c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 2358c2ecf20Sopenharmony_ci .repeat_count = 2, 2368c2ecf20Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT1, 2378c2ecf20Sopenharmony_ci }, { 2388c2ecf20Sopenharmony_ci /* 5: Active lines */ 2398c2ecf20Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 2408c2ecf20Sopenharmony_ci .offset_count = (sig->mode.vsync_len + 2418c2ecf20Sopenharmony_ci sig->mode.vback_porch) / 2, 2428c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_HSYNC, 2438c2ecf20Sopenharmony_ci .repeat_count = sig->mode.vactive / 2, 2448c2ecf20Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT4, 2458c2ecf20Sopenharmony_ci }, { 2468c2ecf20Sopenharmony_ci /* 6: Active pixel, referenced by DC */ 2478c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 2488c2ecf20Sopenharmony_ci .offset_count = sig->mode.hsync_len + 2498c2ecf20Sopenharmony_ci sig->mode.hback_porch, 2508c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 2518c2ecf20Sopenharmony_ci .repeat_count = sig->mode.hactive, 2528c2ecf20Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT5, 2538c2ecf20Sopenharmony_ci }, { 2548c2ecf20Sopenharmony_ci /* 7: Half line HSYNC */ 2558c2ecf20Sopenharmony_ci .run_count = h_total / 2 - 1, 2568c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci }; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void ipu_di_sync_config_noninterlaced(struct ipu_di *di, 2668c2ecf20Sopenharmony_ci struct ipu_di_signal_cfg *sig, int div) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci u32 h_total = sig->mode.hactive + sig->mode.hsync_len + 2698c2ecf20Sopenharmony_ci sig->mode.hback_porch + sig->mode.hfront_porch; 2708c2ecf20Sopenharmony_ci u32 v_total = sig->mode.vactive + sig->mode.vsync_len + 2718c2ecf20Sopenharmony_ci sig->mode.vback_porch + sig->mode.vfront_porch; 2728c2ecf20Sopenharmony_ci struct di_sync_config cfg[] = { 2738c2ecf20Sopenharmony_ci { 2748c2ecf20Sopenharmony_ci /* 1: INT_HSYNC */ 2758c2ecf20Sopenharmony_ci .run_count = h_total - 1, 2768c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 2778c2ecf20Sopenharmony_ci } , { 2788c2ecf20Sopenharmony_ci /* PIN2: HSYNC */ 2798c2ecf20Sopenharmony_ci .run_count = h_total - 1, 2808c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 2818c2ecf20Sopenharmony_ci .offset_count = div * sig->v_to_h_sync, 2828c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 2838c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 2848c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 2858c2ecf20Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 2868c2ecf20Sopenharmony_ci } , { 2878c2ecf20Sopenharmony_ci /* PIN3: VSYNC */ 2888c2ecf20Sopenharmony_ci .run_count = v_total - 1, 2898c2ecf20Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 2908c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 2918c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 2928c2ecf20Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 2938c2ecf20Sopenharmony_ci } , { 2948c2ecf20Sopenharmony_ci /* 4: Line Active */ 2958c2ecf20Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 2968c2ecf20Sopenharmony_ci .offset_count = sig->mode.vsync_len + 2978c2ecf20Sopenharmony_ci sig->mode.vback_porch, 2988c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_HSYNC, 2998c2ecf20Sopenharmony_ci .repeat_count = sig->mode.vactive, 3008c2ecf20Sopenharmony_ci .cnt_clr_src = DI_SYNC_VSYNC, 3018c2ecf20Sopenharmony_ci } , { 3028c2ecf20Sopenharmony_ci /* 5: Pixel Active, referenced by DC */ 3038c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 3048c2ecf20Sopenharmony_ci .offset_count = sig->mode.hsync_len + 3058c2ecf20Sopenharmony_ci sig->mode.hback_porch, 3068c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 3078c2ecf20Sopenharmony_ci .repeat_count = sig->mode.hactive, 3088c2ecf20Sopenharmony_ci .cnt_clr_src = 5, /* Line Active */ 3098c2ecf20Sopenharmony_ci } , { 3108c2ecf20Sopenharmony_ci /* unused */ 3118c2ecf20Sopenharmony_ci } , { 3128c2ecf20Sopenharmony_ci /* unused */ 3138c2ecf20Sopenharmony_ci } , { 3148c2ecf20Sopenharmony_ci /* unused */ 3158c2ecf20Sopenharmony_ci } , { 3168c2ecf20Sopenharmony_ci /* unused */ 3178c2ecf20Sopenharmony_ci }, 3188c2ecf20Sopenharmony_ci }; 3198c2ecf20Sopenharmony_ci /* can't use #7 and #8 for line active and pixel active counters */ 3208c2ecf20Sopenharmony_ci struct di_sync_config cfg_vga[] = { 3218c2ecf20Sopenharmony_ci { 3228c2ecf20Sopenharmony_ci /* 1: INT_HSYNC */ 3238c2ecf20Sopenharmony_ci .run_count = h_total - 1, 3248c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 3258c2ecf20Sopenharmony_ci } , { 3268c2ecf20Sopenharmony_ci /* 2: VSYNC */ 3278c2ecf20Sopenharmony_ci .run_count = v_total - 1, 3288c2ecf20Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 3298c2ecf20Sopenharmony_ci } , { 3308c2ecf20Sopenharmony_ci /* 3: Line Active */ 3318c2ecf20Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 3328c2ecf20Sopenharmony_ci .offset_count = sig->mode.vsync_len + 3338c2ecf20Sopenharmony_ci sig->mode.vback_porch, 3348c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 3358c2ecf20Sopenharmony_ci .repeat_count = sig->mode.vactive, 3368c2ecf20Sopenharmony_ci .cnt_clr_src = 3 /* VSYNC */, 3378c2ecf20Sopenharmony_ci } , { 3388c2ecf20Sopenharmony_ci /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ 3398c2ecf20Sopenharmony_ci .run_count = h_total - 1, 3408c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 3418c2ecf20Sopenharmony_ci .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ 3428c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 3438c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 3448c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 3458c2ecf20Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 3468c2ecf20Sopenharmony_ci } , { 3478c2ecf20Sopenharmony_ci /* 5: Pixel Active signal to DC */ 3488c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 3498c2ecf20Sopenharmony_ci .offset_count = sig->mode.hsync_len + 3508c2ecf20Sopenharmony_ci sig->mode.hback_porch, 3518c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 3528c2ecf20Sopenharmony_ci .repeat_count = sig->mode.hactive, 3538c2ecf20Sopenharmony_ci .cnt_clr_src = 4, /* Line Active */ 3548c2ecf20Sopenharmony_ci } , { 3558c2ecf20Sopenharmony_ci /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ 3568c2ecf20Sopenharmony_ci .run_count = v_total - 1, 3578c2ecf20Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 3588c2ecf20Sopenharmony_ci .offset_count = 1, /* magic value from Freescale TVE driver */ 3598c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 3608c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 3618c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 3628c2ecf20Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 3638c2ecf20Sopenharmony_ci } , { 3648c2ecf20Sopenharmony_ci /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ 3658c2ecf20Sopenharmony_ci .run_count = h_total - 1, 3668c2ecf20Sopenharmony_ci .run_src = DI_SYNC_CLK, 3678c2ecf20Sopenharmony_ci .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ 3688c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_CLK, 3698c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 3708c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 3718c2ecf20Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 3728c2ecf20Sopenharmony_ci } , { 3738c2ecf20Sopenharmony_ci /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ 3748c2ecf20Sopenharmony_ci .run_count = v_total - 1, 3758c2ecf20Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 3768c2ecf20Sopenharmony_ci .offset_count = 1, /* magic value from Freescale TVE driver */ 3778c2ecf20Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 3788c2ecf20Sopenharmony_ci .cnt_polarity_gen_en = 1, 3798c2ecf20Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 3808c2ecf20Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 3818c2ecf20Sopenharmony_ci } , { 3828c2ecf20Sopenharmony_ci /* unused */ 3838c2ecf20Sopenharmony_ci }, 3848c2ecf20Sopenharmony_ci }; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ipu_di_write(di, v_total - 1, DI_SCR_CONF); 3878c2ecf20Sopenharmony_ci if (sig->hsync_pin == 2 && sig->vsync_pin == 3) 3888c2ecf20Sopenharmony_ci ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); 3898c2ecf20Sopenharmony_ci else 3908c2ecf20Sopenharmony_ci ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void ipu_di_config_clock(struct ipu_di *di, 3948c2ecf20Sopenharmony_ci const struct ipu_di_signal_cfg *sig) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct clk *clk; 3978c2ecf20Sopenharmony_ci unsigned clkgen0; 3988c2ecf20Sopenharmony_ci uint32_t val; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (sig->clkflags & IPU_DI_CLKMODE_EXT) { 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * CLKMODE_EXT means we must use the DI clock: this is 4038c2ecf20Sopenharmony_ci * needed for things like LVDS which needs to feed the 4048c2ecf20Sopenharmony_ci * DI and LDB with the same pixel clock. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci clk = di->clk_di; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * CLKMODE_SYNC means that we want the DI to be 4118c2ecf20Sopenharmony_ci * clocked at the same rate as the parent clock. 4128c2ecf20Sopenharmony_ci * This is needed (eg) for LDB which needs to be 4138c2ecf20Sopenharmony_ci * fed with the same pixel clock. We assume that 4148c2ecf20Sopenharmony_ci * the LDB clock has already been set correctly. 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_ci clkgen0 = 1 << 4; 4178c2ecf20Sopenharmony_ci } else { 4188c2ecf20Sopenharmony_ci /* 4198c2ecf20Sopenharmony_ci * We can use the divider. We should really have 4208c2ecf20Sopenharmony_ci * a flag here indicating whether the bridge can 4218c2ecf20Sopenharmony_ci * cope with a fractional divider or not. For the 4228c2ecf20Sopenharmony_ci * time being, let's go for simplicitly and 4238c2ecf20Sopenharmony_ci * reliability. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci unsigned long in_rate; 4268c2ecf20Sopenharmony_ci unsigned div; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci clk_set_rate(clk, sig->mode.pixelclock); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci in_rate = clk_get_rate(clk); 4318c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); 4328c2ecf20Sopenharmony_ci div = clamp(div, 1U, 255U); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci clkgen0 = div << 4; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * For other interfaces, we can arbitarily select between 4398c2ecf20Sopenharmony_ci * the DI specific clock and the internal IPU clock. See 4408c2ecf20Sopenharmony_ci * DI_GENERAL bit 20. We select the IPU clock if it can 4418c2ecf20Sopenharmony_ci * give us a clock rate within 1% of the requested frequency, 4428c2ecf20Sopenharmony_ci * otherwise we use the DI clock. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_ci unsigned long rate, clkrate; 4458c2ecf20Sopenharmony_ci unsigned div, error; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci clkrate = clk_get_rate(di->clk_ipu); 4488c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); 4498c2ecf20Sopenharmony_ci div = clamp(div, 1U, 255U); 4508c2ecf20Sopenharmony_ci rate = clkrate / div; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci error = rate / (sig->mode.pixelclock / 1000); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %c%d.%d%%\n", 4558c2ecf20Sopenharmony_ci rate, div, error < 1000 ? '-' : '+', 4568c2ecf20Sopenharmony_ci abs(error - 1000) / 10, abs(error - 1000) % 10); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Allow a 1% error */ 4598c2ecf20Sopenharmony_ci if (error < 1010 && error >= 990) { 4608c2ecf20Sopenharmony_ci clk = di->clk_ipu; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci clkgen0 = div << 4; 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci unsigned long in_rate; 4658c2ecf20Sopenharmony_ci unsigned div; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci clk = di->clk_di; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci clk_set_rate(clk, sig->mode.pixelclock); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci in_rate = clk_get_rate(clk); 4728c2ecf20Sopenharmony_ci div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); 4738c2ecf20Sopenharmony_ci div = clamp(div, 1U, 255U); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci clkgen0 = div << 4; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci di->clk_di_pixel = clk; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Set the divider */ 4828c2ecf20Sopenharmony_ci ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * Set the high/low periods. Bits 24:16 give us the falling edge, 4868c2ecf20Sopenharmony_ci * and bits 8:0 give the rising edge. LSB is fraction, and is 4878c2ecf20Sopenharmony_ci * based on the divider above. We want a 50% duty cycle, so set 4888c2ecf20Sopenharmony_ci * the falling edge to be half the divider. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Finally select the input clock */ 4938c2ecf20Sopenharmony_ci val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; 4948c2ecf20Sopenharmony_ci if (clk == di->clk_di) 4958c2ecf20Sopenharmony_ci val |= DI_GEN_DI_CLK_EXT; 4968c2ecf20Sopenharmony_ci ipu_di_write(di, val, DI_GENERAL); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", 4998c2ecf20Sopenharmony_ci sig->mode.pixelclock, 5008c2ecf20Sopenharmony_ci clk_get_rate(di->clk_ipu), 5018c2ecf20Sopenharmony_ci clk_get_rate(di->clk_di), 5028c2ecf20Sopenharmony_ci clk == di->clk_di ? "DI" : "IPU", 5038c2ecf20Sopenharmony_ci clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * This function is called to adjust a video mode to IPU restrictions. 5088c2ecf20Sopenharmony_ci * It is meant to be called from drm crtc mode_fixup() methods. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ciint ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci u32 diff; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (mode->vfront_porch >= 2) 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci diff = 2 - mode->vfront_porch; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (mode->vback_porch >= diff) { 5208c2ecf20Sopenharmony_ci mode->vfront_porch = 2; 5218c2ecf20Sopenharmony_ci mode->vback_porch -= diff; 5228c2ecf20Sopenharmony_ci } else if (mode->vsync_len > diff) { 5238c2ecf20Sopenharmony_ci mode->vfront_porch = 2; 5248c2ecf20Sopenharmony_ci mode->vsync_len = mode->vsync_len - diff; 5258c2ecf20Sopenharmony_ci } else { 5268c2ecf20Sopenharmony_ci dev_warn(di->ipu->dev, "failed to adjust videomode\n"); 5278c2ecf20Sopenharmony_ci return -EINVAL; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic u32 ipu_di_gen_polarity(int pin) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci switch (pin) { 5388c2ecf20Sopenharmony_ci case 1: 5398c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_1; 5408c2ecf20Sopenharmony_ci case 2: 5418c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_2; 5428c2ecf20Sopenharmony_ci case 3: 5438c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_3; 5448c2ecf20Sopenharmony_ci case 4: 5458c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_4; 5468c2ecf20Sopenharmony_ci case 5: 5478c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_5; 5488c2ecf20Sopenharmony_ci case 6: 5498c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_6; 5508c2ecf20Sopenharmony_ci case 7: 5518c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_7; 5528c2ecf20Sopenharmony_ci case 8: 5538c2ecf20Sopenharmony_ci return DI_GEN_POLARITY_8; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciint ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci u32 reg; 5618c2ecf20Sopenharmony_ci u32 di_gen, vsync_cnt; 5628c2ecf20Sopenharmony_ci u32 div; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", 5658c2ecf20Sopenharmony_ci di->id, sig->mode.hactive, sig->mode.vactive); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", 5688c2ecf20Sopenharmony_ci clk_get_rate(di->clk_ipu), 5698c2ecf20Sopenharmony_ci clk_get_rate(di->clk_di), 5708c2ecf20Sopenharmony_ci sig->mode.pixelclock); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci mutex_lock(&di_mutex); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ipu_di_config_clock(di, sig); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; 5778c2ecf20Sopenharmony_ci div = div / 16; /* Now divider is integer portion */ 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Setup pixel clock timing */ 5808c2ecf20Sopenharmony_ci /* Down time is half of period */ 5818c2ecf20Sopenharmony_ci ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); 5848c2ecf20Sopenharmony_ci ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; 5878c2ecf20Sopenharmony_ci di_gen |= DI_GEN_DI_VSYNC_EXT; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { 5908c2ecf20Sopenharmony_ci ipu_di_sync_config_interlaced(di, sig); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* set y_sel = 1 */ 5938c2ecf20Sopenharmony_ci di_gen |= 0x10000000; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci vsync_cnt = 3; 5968c2ecf20Sopenharmony_ci } else { 5978c2ecf20Sopenharmony_ci ipu_di_sync_config_noninterlaced(di, sig, div); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci vsync_cnt = 3; 6008c2ecf20Sopenharmony_ci if (di->id == 1) 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * TODO: change only for TVEv2, parallel display 6038c2ecf20Sopenharmony_ci * uses pin 2 / 3 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_ci if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) 6068c2ecf20Sopenharmony_ci vsync_cnt = 6; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) 6108c2ecf20Sopenharmony_ci di_gen |= ipu_di_gen_polarity(sig->hsync_pin); 6118c2ecf20Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) 6128c2ecf20Sopenharmony_ci di_gen |= ipu_di_gen_polarity(sig->vsync_pin); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (sig->clk_pol) 6158c2ecf20Sopenharmony_ci di_gen |= DI_GEN_POLARITY_DISP_CLK; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ipu_di_write(di, di_gen, DI_GENERAL); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, 6208c2ecf20Sopenharmony_ci DI_SYNC_AS_GEN); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci reg = ipu_di_read(di, DI_POL); 6238c2ecf20Sopenharmony_ci reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (sig->enable_pol) 6268c2ecf20Sopenharmony_ci reg |= DI_POL_DRDY_POLARITY_15; 6278c2ecf20Sopenharmony_ci if (sig->data_pol) 6288c2ecf20Sopenharmony_ci reg |= DI_POL_DRDY_DATA_POLARITY; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ipu_di_write(di, reg, DI_POL); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci mutex_unlock(&di_mutex); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ciint ipu_di_enable(struct ipu_di *di) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci int ret; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(di->clk_di_pixel)); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = clk_prepare_enable(di->clk_di_pixel); 6458c2ecf20Sopenharmony_ci if (ret) 6468c2ecf20Sopenharmony_ci return ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ipu_module_enable(di->ipu, di->module); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_enable); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ciint ipu_di_disable(struct ipu_di *di) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(di->clk_di_pixel)); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci ipu_module_disable(di->ipu, di->module); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci clk_disable_unprepare(di->clk_di_pixel); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_disable); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciint ipu_di_get_num(struct ipu_di *di) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci return di->id; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_get_num); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipu_di_lock); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistruct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct ipu_di *di; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (disp > 1) 6798c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci di = ipu->di_priv[disp]; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci mutex_lock(&ipu_di_lock); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (di->inuse) { 6868c2ecf20Sopenharmony_ci di = ERR_PTR(-EBUSY); 6878c2ecf20Sopenharmony_ci goto out; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci di->inuse = true; 6918c2ecf20Sopenharmony_ciout: 6928c2ecf20Sopenharmony_ci mutex_unlock(&ipu_di_lock); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return di; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_get); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_civoid ipu_di_put(struct ipu_di *di) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci mutex_lock(&ipu_di_lock); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci di->inuse = false; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci mutex_unlock(&ipu_di_lock); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_put); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ciint ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, 7098c2ecf20Sopenharmony_ci unsigned long base, 7108c2ecf20Sopenharmony_ci u32 module, struct clk *clk_ipu) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct ipu_di *di; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (id > 1) 7158c2ecf20Sopenharmony_ci return -ENODEV; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 7188c2ecf20Sopenharmony_ci if (!di) 7198c2ecf20Sopenharmony_ci return -ENOMEM; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ipu->di_priv[id] = di; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); 7248c2ecf20Sopenharmony_ci if (IS_ERR(di->clk_di)) 7258c2ecf20Sopenharmony_ci return PTR_ERR(di->clk_di); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci di->module = module; 7288c2ecf20Sopenharmony_ci di->id = id; 7298c2ecf20Sopenharmony_ci di->clk_ipu = clk_ipu; 7308c2ecf20Sopenharmony_ci di->base = devm_ioremap(dev, base, PAGE_SIZE); 7318c2ecf20Sopenharmony_ci if (!di->base) 7328c2ecf20Sopenharmony_ci return -ENOMEM; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ipu_di_write(di, 0x10, DI_BS_CLKGEN0); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", 7378c2ecf20Sopenharmony_ci id, base, di->base); 7388c2ecf20Sopenharmony_ci di->inuse = false; 7398c2ecf20Sopenharmony_ci di->ipu = ipu; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return 0; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_civoid ipu_di_exit(struct ipu_soc *ipu, int id) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci} 747