162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> 462306a36Sopenharmony_ci * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/export.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <video/imx-ipu-v3.h> 1562306a36Sopenharmony_ci#include "ipu-prv.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct ipu_di { 1862306a36Sopenharmony_ci void __iomem *base; 1962306a36Sopenharmony_ci int id; 2062306a36Sopenharmony_ci u32 module; 2162306a36Sopenharmony_ci struct clk *clk_di; /* display input clock */ 2262306a36Sopenharmony_ci struct clk *clk_ipu; /* IPU bus clock */ 2362306a36Sopenharmony_ci struct clk *clk_di_pixel; /* resulting pixel clock */ 2462306a36Sopenharmony_ci bool inuse; 2562306a36Sopenharmony_ci struct ipu_soc *ipu; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(di_mutex); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct di_sync_config { 3162306a36Sopenharmony_ci int run_count; 3262306a36Sopenharmony_ci int run_src; 3362306a36Sopenharmony_ci int offset_count; 3462306a36Sopenharmony_ci int offset_src; 3562306a36Sopenharmony_ci int repeat_count; 3662306a36Sopenharmony_ci int cnt_clr_src; 3762306a36Sopenharmony_ci int cnt_polarity_gen_en; 3862306a36Sopenharmony_ci int cnt_polarity_clr_src; 3962306a36Sopenharmony_ci int cnt_polarity_trigger_src; 4062306a36Sopenharmony_ci int cnt_up; 4162306a36Sopenharmony_ci int cnt_down; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum di_pins { 4562306a36Sopenharmony_ci DI_PIN11 = 0, 4662306a36Sopenharmony_ci DI_PIN12 = 1, 4762306a36Sopenharmony_ci DI_PIN13 = 2, 4862306a36Sopenharmony_ci DI_PIN14 = 3, 4962306a36Sopenharmony_ci DI_PIN15 = 4, 5062306a36Sopenharmony_ci DI_PIN16 = 5, 5162306a36Sopenharmony_ci DI_PIN17 = 6, 5262306a36Sopenharmony_ci DI_PIN_CS = 7, 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci DI_PIN_SER_CLK = 0, 5562306a36Sopenharmony_ci DI_PIN_SER_RS = 1, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cienum di_sync_wave { 5962306a36Sopenharmony_ci DI_SYNC_NONE = 0, 6062306a36Sopenharmony_ci DI_SYNC_CLK = 1, 6162306a36Sopenharmony_ci DI_SYNC_INT_HSYNC = 2, 6262306a36Sopenharmony_ci DI_SYNC_HSYNC = 3, 6362306a36Sopenharmony_ci DI_SYNC_VSYNC = 4, 6462306a36Sopenharmony_ci DI_SYNC_DE = 6, 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci DI_SYNC_CNT1 = 2, /* counter >= 2 only */ 6762306a36Sopenharmony_ci DI_SYNC_CNT4 = 5, /* counter >= 5 only */ 6862306a36Sopenharmony_ci DI_SYNC_CNT5 = 6, /* counter >= 6 only */ 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define SYNC_WAVE 0 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DI_GENERAL 0x0000 7462306a36Sopenharmony_ci#define DI_BS_CLKGEN0 0x0004 7562306a36Sopenharmony_ci#define DI_BS_CLKGEN1 0x0008 7662306a36Sopenharmony_ci#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) 7762306a36Sopenharmony_ci#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) 7862306a36Sopenharmony_ci#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) 7962306a36Sopenharmony_ci#define DI_SYNC_AS_GEN 0x0054 8062306a36Sopenharmony_ci#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) 8162306a36Sopenharmony_ci#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) 8262306a36Sopenharmony_ci#define DI_SER_CONF 0x015c 8362306a36Sopenharmony_ci#define DI_SSC 0x0160 8462306a36Sopenharmony_ci#define DI_POL 0x0164 8562306a36Sopenharmony_ci#define DI_AW0 0x0168 8662306a36Sopenharmony_ci#define DI_AW1 0x016c 8762306a36Sopenharmony_ci#define DI_SCR_CONF 0x0170 8862306a36Sopenharmony_ci#define DI_STAT 0x0174 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) 9162306a36Sopenharmony_ci#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) 9262306a36Sopenharmony_ci#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) 9362306a36Sopenharmony_ci#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) 9662306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) 9762306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) 9862306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) 9962306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) 10062306a36Sopenharmony_ci#define DI_SW_GEN1_CNT_UP(x) (x) 10162306a36Sopenharmony_ci#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 10462306a36Sopenharmony_ci#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define DI_GEN_POLARITY_1 (1 << 0) 10762306a36Sopenharmony_ci#define DI_GEN_POLARITY_2 (1 << 1) 10862306a36Sopenharmony_ci#define DI_GEN_POLARITY_3 (1 << 2) 10962306a36Sopenharmony_ci#define DI_GEN_POLARITY_4 (1 << 3) 11062306a36Sopenharmony_ci#define DI_GEN_POLARITY_5 (1 << 4) 11162306a36Sopenharmony_ci#define DI_GEN_POLARITY_6 (1 << 5) 11262306a36Sopenharmony_ci#define DI_GEN_POLARITY_7 (1 << 6) 11362306a36Sopenharmony_ci#define DI_GEN_POLARITY_8 (1 << 7) 11462306a36Sopenharmony_ci#define DI_GEN_POLARITY_DISP_CLK (1 << 17) 11562306a36Sopenharmony_ci#define DI_GEN_DI_CLK_EXT (1 << 20) 11662306a36Sopenharmony_ci#define DI_GEN_DI_VSYNC_EXT (1 << 21) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define DI_POL_DRDY_DATA_POLARITY (1 << 7) 11962306a36Sopenharmony_ci#define DI_POL_DRDY_POLARITY_15 (1 << 4) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define DI_VSYNC_SEL_OFFSET 13 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return readl(di->base + offset); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci writel(value, di->base + offset); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void ipu_di_data_wave_config(struct ipu_di *di, 13462306a36Sopenharmony_ci int wave_gen, 13562306a36Sopenharmony_ci int access_size, int component_size) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 reg; 13862306a36Sopenharmony_ci reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | 13962306a36Sopenharmony_ci (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); 14062306a36Sopenharmony_ci ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, 14462306a36Sopenharmony_ci int set, int up, int down) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 reg; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); 14962306a36Sopenharmony_ci reg &= ~(0x3 << (di_pin * 2)); 15062306a36Sopenharmony_ci reg |= set << (di_pin * 2); 15162306a36Sopenharmony_ci ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, 15762306a36Sopenharmony_ci int start, int count) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci u32 reg; 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 16362306a36Sopenharmony_ci struct di_sync_config *c = &config[i]; 16462306a36Sopenharmony_ci int wave_gen = start + i + 1; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || 16762306a36Sopenharmony_ci (c->repeat_count >= 0x1000) || 16862306a36Sopenharmony_ci (c->cnt_up >= 0x400) || 16962306a36Sopenharmony_ci (c->cnt_down >= 0x400)) { 17062306a36Sopenharmony_ci dev_err(di->ipu->dev, "DI%d counters out of range.\n", 17162306a36Sopenharmony_ci di->id); 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | 17662306a36Sopenharmony_ci DI_SW_GEN0_RUN_SRC(c->run_src) | 17762306a36Sopenharmony_ci DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | 17862306a36Sopenharmony_ci DI_SW_GEN0_OFFSET_SRC(c->offset_src); 17962306a36Sopenharmony_ci ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | 18262306a36Sopenharmony_ci DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | 18362306a36Sopenharmony_ci DI_SW_GEN1_CNT_POL_TRIGGER_SRC( 18462306a36Sopenharmony_ci c->cnt_polarity_trigger_src) | 18562306a36Sopenharmony_ci DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | 18662306a36Sopenharmony_ci DI_SW_GEN1_CNT_DOWN(c->cnt_down) | 18762306a36Sopenharmony_ci DI_SW_GEN1_CNT_UP(c->cnt_up); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Enable auto reload */ 19062306a36Sopenharmony_ci if (c->repeat_count == 0) 19162306a36Sopenharmony_ci reg |= DI_SW_GEN1_AUTO_RELOAD; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci reg = ipu_di_read(di, DI_STP_REP(wave_gen)); 19662306a36Sopenharmony_ci reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); 19762306a36Sopenharmony_ci reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); 19862306a36Sopenharmony_ci ipu_di_write(di, reg, DI_STP_REP(wave_gen)); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void ipu_di_sync_config_interlaced(struct ipu_di *di, 20362306a36Sopenharmony_ci struct ipu_di_signal_cfg *sig) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 h_total = sig->mode.hactive + sig->mode.hsync_len + 20662306a36Sopenharmony_ci sig->mode.hback_porch + sig->mode.hfront_porch; 20762306a36Sopenharmony_ci u32 v_total = sig->mode.vactive + sig->mode.vsync_len + 20862306a36Sopenharmony_ci sig->mode.vback_porch + sig->mode.vfront_porch; 20962306a36Sopenharmony_ci struct di_sync_config cfg[] = { 21062306a36Sopenharmony_ci { 21162306a36Sopenharmony_ci /* 1: internal VSYNC for each frame */ 21262306a36Sopenharmony_ci .run_count = v_total * 2 - 1, 21362306a36Sopenharmony_ci .run_src = 3, /* == counter 7 */ 21462306a36Sopenharmony_ci }, { 21562306a36Sopenharmony_ci /* PIN2: HSYNC waveform */ 21662306a36Sopenharmony_ci .run_count = h_total - 1, 21762306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 21862306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 21962306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 22062306a36Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 22162306a36Sopenharmony_ci }, { 22262306a36Sopenharmony_ci /* PIN3: VSYNC waveform */ 22362306a36Sopenharmony_ci .run_count = v_total - 1, 22462306a36Sopenharmony_ci .run_src = 4, /* == counter 7 */ 22562306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 22662306a36Sopenharmony_ci .cnt_polarity_trigger_src = 4, /* == counter 7 */ 22762306a36Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 22862306a36Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT1, 22962306a36Sopenharmony_ci }, { 23062306a36Sopenharmony_ci /* 4: Field */ 23162306a36Sopenharmony_ci .run_count = v_total / 2, 23262306a36Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 23362306a36Sopenharmony_ci .offset_count = h_total / 2, 23462306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 23562306a36Sopenharmony_ci .repeat_count = 2, 23662306a36Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT1, 23762306a36Sopenharmony_ci }, { 23862306a36Sopenharmony_ci /* 5: Active lines */ 23962306a36Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 24062306a36Sopenharmony_ci .offset_count = (sig->mode.vsync_len + 24162306a36Sopenharmony_ci sig->mode.vback_porch) / 2, 24262306a36Sopenharmony_ci .offset_src = DI_SYNC_HSYNC, 24362306a36Sopenharmony_ci .repeat_count = sig->mode.vactive / 2, 24462306a36Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT4, 24562306a36Sopenharmony_ci }, { 24662306a36Sopenharmony_ci /* 6: Active pixel, referenced by DC */ 24762306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 24862306a36Sopenharmony_ci .offset_count = sig->mode.hsync_len + 24962306a36Sopenharmony_ci sig->mode.hback_porch, 25062306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 25162306a36Sopenharmony_ci .repeat_count = sig->mode.hactive, 25262306a36Sopenharmony_ci .cnt_clr_src = DI_SYNC_CNT5, 25362306a36Sopenharmony_ci }, { 25462306a36Sopenharmony_ci /* 7: Half line HSYNC */ 25562306a36Sopenharmony_ci .run_count = h_total / 2 - 1, 25662306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci }; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void ipu_di_sync_config_noninterlaced(struct ipu_di *di, 26662306a36Sopenharmony_ci struct ipu_di_signal_cfg *sig, int div) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u32 h_total = sig->mode.hactive + sig->mode.hsync_len + 26962306a36Sopenharmony_ci sig->mode.hback_porch + sig->mode.hfront_porch; 27062306a36Sopenharmony_ci u32 v_total = sig->mode.vactive + sig->mode.vsync_len + 27162306a36Sopenharmony_ci sig->mode.vback_porch + sig->mode.vfront_porch; 27262306a36Sopenharmony_ci struct di_sync_config cfg[] = { 27362306a36Sopenharmony_ci { 27462306a36Sopenharmony_ci /* 1: INT_HSYNC */ 27562306a36Sopenharmony_ci .run_count = h_total - 1, 27662306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 27762306a36Sopenharmony_ci } , { 27862306a36Sopenharmony_ci /* PIN2: HSYNC */ 27962306a36Sopenharmony_ci .run_count = h_total - 1, 28062306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 28162306a36Sopenharmony_ci .offset_count = div * sig->v_to_h_sync, 28262306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 28362306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 28462306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 28562306a36Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 28662306a36Sopenharmony_ci } , { 28762306a36Sopenharmony_ci /* PIN3: VSYNC */ 28862306a36Sopenharmony_ci .run_count = v_total - 1, 28962306a36Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 29062306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 29162306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 29262306a36Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 29362306a36Sopenharmony_ci } , { 29462306a36Sopenharmony_ci /* 4: Line Active */ 29562306a36Sopenharmony_ci .run_src = DI_SYNC_HSYNC, 29662306a36Sopenharmony_ci .offset_count = sig->mode.vsync_len + 29762306a36Sopenharmony_ci sig->mode.vback_porch, 29862306a36Sopenharmony_ci .offset_src = DI_SYNC_HSYNC, 29962306a36Sopenharmony_ci .repeat_count = sig->mode.vactive, 30062306a36Sopenharmony_ci .cnt_clr_src = DI_SYNC_VSYNC, 30162306a36Sopenharmony_ci } , { 30262306a36Sopenharmony_ci /* 5: Pixel Active, referenced by DC */ 30362306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 30462306a36Sopenharmony_ci .offset_count = sig->mode.hsync_len + 30562306a36Sopenharmony_ci sig->mode.hback_porch, 30662306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 30762306a36Sopenharmony_ci .repeat_count = sig->mode.hactive, 30862306a36Sopenharmony_ci .cnt_clr_src = 5, /* Line Active */ 30962306a36Sopenharmony_ci } , { 31062306a36Sopenharmony_ci /* unused */ 31162306a36Sopenharmony_ci } , { 31262306a36Sopenharmony_ci /* unused */ 31362306a36Sopenharmony_ci }, 31462306a36Sopenharmony_ci }; 31562306a36Sopenharmony_ci /* can't use #7 and #8 for line active and pixel active counters */ 31662306a36Sopenharmony_ci struct di_sync_config cfg_vga[] = { 31762306a36Sopenharmony_ci { 31862306a36Sopenharmony_ci /* 1: INT_HSYNC */ 31962306a36Sopenharmony_ci .run_count = h_total - 1, 32062306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 32162306a36Sopenharmony_ci } , { 32262306a36Sopenharmony_ci /* 2: VSYNC */ 32362306a36Sopenharmony_ci .run_count = v_total - 1, 32462306a36Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 32562306a36Sopenharmony_ci } , { 32662306a36Sopenharmony_ci /* 3: Line Active */ 32762306a36Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 32862306a36Sopenharmony_ci .offset_count = sig->mode.vsync_len + 32962306a36Sopenharmony_ci sig->mode.vback_porch, 33062306a36Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 33162306a36Sopenharmony_ci .repeat_count = sig->mode.vactive, 33262306a36Sopenharmony_ci .cnt_clr_src = 3 /* VSYNC */, 33362306a36Sopenharmony_ci } , { 33462306a36Sopenharmony_ci /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ 33562306a36Sopenharmony_ci .run_count = h_total - 1, 33662306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 33762306a36Sopenharmony_ci .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ 33862306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 33962306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 34062306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 34162306a36Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 34262306a36Sopenharmony_ci } , { 34362306a36Sopenharmony_ci /* 5: Pixel Active signal to DC */ 34462306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 34562306a36Sopenharmony_ci .offset_count = sig->mode.hsync_len + 34662306a36Sopenharmony_ci sig->mode.hback_porch, 34762306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 34862306a36Sopenharmony_ci .repeat_count = sig->mode.hactive, 34962306a36Sopenharmony_ci .cnt_clr_src = 4, /* Line Active */ 35062306a36Sopenharmony_ci } , { 35162306a36Sopenharmony_ci /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ 35262306a36Sopenharmony_ci .run_count = v_total - 1, 35362306a36Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 35462306a36Sopenharmony_ci .offset_count = 1, /* magic value from Freescale TVE driver */ 35562306a36Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 35662306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 35762306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 35862306a36Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 35962306a36Sopenharmony_ci } , { 36062306a36Sopenharmony_ci /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ 36162306a36Sopenharmony_ci .run_count = h_total - 1, 36262306a36Sopenharmony_ci .run_src = DI_SYNC_CLK, 36362306a36Sopenharmony_ci .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ 36462306a36Sopenharmony_ci .offset_src = DI_SYNC_CLK, 36562306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 36662306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_CLK, 36762306a36Sopenharmony_ci .cnt_down = sig->mode.hsync_len * 2, 36862306a36Sopenharmony_ci } , { 36962306a36Sopenharmony_ci /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ 37062306a36Sopenharmony_ci .run_count = v_total - 1, 37162306a36Sopenharmony_ci .run_src = DI_SYNC_INT_HSYNC, 37262306a36Sopenharmony_ci .offset_count = 1, /* magic value from Freescale TVE driver */ 37362306a36Sopenharmony_ci .offset_src = DI_SYNC_INT_HSYNC, 37462306a36Sopenharmony_ci .cnt_polarity_gen_en = 1, 37562306a36Sopenharmony_ci .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, 37662306a36Sopenharmony_ci .cnt_down = sig->mode.vsync_len * 2, 37762306a36Sopenharmony_ci } , { 37862306a36Sopenharmony_ci /* unused */ 37962306a36Sopenharmony_ci }, 38062306a36Sopenharmony_ci }; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ipu_di_write(di, v_total - 1, DI_SCR_CONF); 38362306a36Sopenharmony_ci if (sig->hsync_pin == 2 && sig->vsync_pin == 3) 38462306a36Sopenharmony_ci ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void ipu_di_config_clock(struct ipu_di *di, 39062306a36Sopenharmony_ci const struct ipu_di_signal_cfg *sig) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct clk *clk; 39362306a36Sopenharmony_ci unsigned clkgen0; 39462306a36Sopenharmony_ci uint32_t val; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (sig->clkflags & IPU_DI_CLKMODE_EXT) { 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * CLKMODE_EXT means we must use the DI clock: this is 39962306a36Sopenharmony_ci * needed for things like LVDS which needs to feed the 40062306a36Sopenharmony_ci * DI and LDB with the same pixel clock. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci clk = di->clk_di; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * CLKMODE_SYNC means that we want the DI to be 40762306a36Sopenharmony_ci * clocked at the same rate as the parent clock. 40862306a36Sopenharmony_ci * This is needed (eg) for LDB which needs to be 40962306a36Sopenharmony_ci * fed with the same pixel clock. We assume that 41062306a36Sopenharmony_ci * the LDB clock has already been set correctly. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci clkgen0 = 1 << 4; 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * We can use the divider. We should really have 41662306a36Sopenharmony_ci * a flag here indicating whether the bridge can 41762306a36Sopenharmony_ci * cope with a fractional divider or not. For the 41862306a36Sopenharmony_ci * time being, let's go for simplicitly and 41962306a36Sopenharmony_ci * reliability. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci unsigned long in_rate; 42262306a36Sopenharmony_ci unsigned div; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci clk_set_rate(clk, sig->mode.pixelclock); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci in_rate = clk_get_rate(clk); 42762306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); 42862306a36Sopenharmony_ci div = clamp(div, 1U, 255U); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci clkgen0 = div << 4; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } else { 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * For other interfaces, we can arbitarily select between 43562306a36Sopenharmony_ci * the DI specific clock and the internal IPU clock. See 43662306a36Sopenharmony_ci * DI_GENERAL bit 20. We select the IPU clock if it can 43762306a36Sopenharmony_ci * give us a clock rate within 1% of the requested frequency, 43862306a36Sopenharmony_ci * otherwise we use the DI clock. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci unsigned long rate, clkrate; 44162306a36Sopenharmony_ci unsigned div, error; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci clkrate = clk_get_rate(di->clk_ipu); 44462306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); 44562306a36Sopenharmony_ci div = clamp(div, 1U, 255U); 44662306a36Sopenharmony_ci rate = clkrate / div; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci error = rate / (sig->mode.pixelclock / 1000); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %c%d.%d%%\n", 45162306a36Sopenharmony_ci rate, div, error < 1000 ? '-' : '+', 45262306a36Sopenharmony_ci abs(error - 1000) / 10, abs(error - 1000) % 10); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* Allow a 1% error */ 45562306a36Sopenharmony_ci if (error < 1010 && error >= 990) { 45662306a36Sopenharmony_ci clk = di->clk_ipu; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci clkgen0 = div << 4; 45962306a36Sopenharmony_ci } else { 46062306a36Sopenharmony_ci unsigned long in_rate; 46162306a36Sopenharmony_ci unsigned div; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci clk = di->clk_di; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci clk_set_rate(clk, sig->mode.pixelclock); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci in_rate = clk_get_rate(clk); 46862306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); 46962306a36Sopenharmony_ci div = clamp(div, 1U, 255U); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci clkgen0 = div << 4; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci di->clk_di_pixel = clk; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Set the divider */ 47862306a36Sopenharmony_ci ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Set the high/low periods. Bits 24:16 give us the falling edge, 48262306a36Sopenharmony_ci * and bits 8:0 give the rising edge. LSB is fraction, and is 48362306a36Sopenharmony_ci * based on the divider above. We want a 50% duty cycle, so set 48462306a36Sopenharmony_ci * the falling edge to be half the divider. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Finally select the input clock */ 48962306a36Sopenharmony_ci val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; 49062306a36Sopenharmony_ci if (clk == di->clk_di) 49162306a36Sopenharmony_ci val |= DI_GEN_DI_CLK_EXT; 49262306a36Sopenharmony_ci ipu_di_write(di, val, DI_GENERAL); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", 49562306a36Sopenharmony_ci sig->mode.pixelclock, 49662306a36Sopenharmony_ci clk_get_rate(di->clk_ipu), 49762306a36Sopenharmony_ci clk_get_rate(di->clk_di), 49862306a36Sopenharmony_ci clk == di->clk_di ? "DI" : "IPU", 49962306a36Sopenharmony_ci clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * This function is called to adjust a video mode to IPU restrictions. 50462306a36Sopenharmony_ci * It is meant to be called from drm crtc mode_fixup() methods. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ciint ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci u32 diff; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!IS_ALIGNED(mode->hactive, 8) && 51162306a36Sopenharmony_ci mode->hfront_porch < ALIGN(mode->hactive, 8) - mode->hactive) { 51262306a36Sopenharmony_ci dev_err(di->ipu->dev, "hactive %d is not aligned to 8 and front porch is too small to compensate\n", 51362306a36Sopenharmony_ci mode->hactive); 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (mode->vfront_porch >= 2) 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci diff = 2 - mode->vfront_porch; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (mode->vback_porch >= diff) { 52362306a36Sopenharmony_ci mode->vfront_porch = 2; 52462306a36Sopenharmony_ci mode->vback_porch -= diff; 52562306a36Sopenharmony_ci } else if (mode->vsync_len > diff) { 52662306a36Sopenharmony_ci mode->vfront_porch = 2; 52762306a36Sopenharmony_ci mode->vsync_len = mode->vsync_len - diff; 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci dev_warn(di->ipu->dev, "failed to adjust videomode\n"); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic u32 ipu_di_gen_polarity(int pin) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci switch (pin) { 54162306a36Sopenharmony_ci case 1: 54262306a36Sopenharmony_ci return DI_GEN_POLARITY_1; 54362306a36Sopenharmony_ci case 2: 54462306a36Sopenharmony_ci return DI_GEN_POLARITY_2; 54562306a36Sopenharmony_ci case 3: 54662306a36Sopenharmony_ci return DI_GEN_POLARITY_3; 54762306a36Sopenharmony_ci case 4: 54862306a36Sopenharmony_ci return DI_GEN_POLARITY_4; 54962306a36Sopenharmony_ci case 5: 55062306a36Sopenharmony_ci return DI_GEN_POLARITY_5; 55162306a36Sopenharmony_ci case 6: 55262306a36Sopenharmony_ci return DI_GEN_POLARITY_6; 55362306a36Sopenharmony_ci case 7: 55462306a36Sopenharmony_ci return DI_GEN_POLARITY_7; 55562306a36Sopenharmony_ci case 8: 55662306a36Sopenharmony_ci return DI_GEN_POLARITY_8; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciint ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci u32 reg; 56462306a36Sopenharmony_ci u32 di_gen, vsync_cnt; 56562306a36Sopenharmony_ci u32 div; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", 56862306a36Sopenharmony_ci di->id, sig->mode.hactive, sig->mode.vactive); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", 57162306a36Sopenharmony_ci clk_get_rate(di->clk_ipu), 57262306a36Sopenharmony_ci clk_get_rate(di->clk_di), 57362306a36Sopenharmony_ci sig->mode.pixelclock); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci mutex_lock(&di_mutex); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ipu_di_config_clock(di, sig); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; 58062306a36Sopenharmony_ci div = div / 16; /* Now divider is integer portion */ 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Setup pixel clock timing */ 58362306a36Sopenharmony_ci /* Down time is half of period */ 58462306a36Sopenharmony_ci ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); 58762306a36Sopenharmony_ci ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; 59062306a36Sopenharmony_ci di_gen |= DI_GEN_DI_VSYNC_EXT; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { 59362306a36Sopenharmony_ci ipu_di_sync_config_interlaced(di, sig); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* set y_sel = 1 */ 59662306a36Sopenharmony_ci di_gen |= 0x10000000; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci vsync_cnt = 3; 59962306a36Sopenharmony_ci } else { 60062306a36Sopenharmony_ci ipu_di_sync_config_noninterlaced(di, sig, div); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci vsync_cnt = 3; 60362306a36Sopenharmony_ci if (di->id == 1) 60462306a36Sopenharmony_ci /* 60562306a36Sopenharmony_ci * TODO: change only for TVEv2, parallel display 60662306a36Sopenharmony_ci * uses pin 2 / 3 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) 60962306a36Sopenharmony_ci vsync_cnt = 6; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) 61362306a36Sopenharmony_ci di_gen |= ipu_di_gen_polarity(sig->hsync_pin); 61462306a36Sopenharmony_ci if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) 61562306a36Sopenharmony_ci di_gen |= ipu_di_gen_polarity(sig->vsync_pin); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (sig->clk_pol) 61862306a36Sopenharmony_ci di_gen |= DI_GEN_POLARITY_DISP_CLK; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ipu_di_write(di, di_gen, DI_GENERAL); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, 62362306a36Sopenharmony_ci DI_SYNC_AS_GEN); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci reg = ipu_di_read(di, DI_POL); 62662306a36Sopenharmony_ci reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (sig->enable_pol) 62962306a36Sopenharmony_ci reg |= DI_POL_DRDY_POLARITY_15; 63062306a36Sopenharmony_ci if (sig->data_pol) 63162306a36Sopenharmony_ci reg |= DI_POL_DRDY_DATA_POLARITY; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ipu_di_write(di, reg, DI_POL); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci mutex_unlock(&di_mutex); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ciint ipu_di_enable(struct ipu_di *di) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int ret; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci WARN_ON(IS_ERR(di->clk_di_pixel)); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci ret = clk_prepare_enable(di->clk_di_pixel); 64862306a36Sopenharmony_ci if (ret) 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ipu_module_enable(di->ipu, di->module); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_enable); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ciint ipu_di_disable(struct ipu_di *di) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci WARN_ON(IS_ERR(di->clk_di_pixel)); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ipu_module_disable(di->ipu, di->module); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci clk_disable_unprepare(di->clk_di_pixel); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_disable); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciint ipu_di_get_num(struct ipu_di *di) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci return di->id; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_get_num); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic DEFINE_MUTEX(ipu_di_lock); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistruct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct ipu_di *di; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (disp > 1) 68262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci di = ipu->di_priv[disp]; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci mutex_lock(&ipu_di_lock); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (di->inuse) { 68962306a36Sopenharmony_ci di = ERR_PTR(-EBUSY); 69062306a36Sopenharmony_ci goto out; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci di->inuse = true; 69462306a36Sopenharmony_ciout: 69562306a36Sopenharmony_ci mutex_unlock(&ipu_di_lock); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return di; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_get); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_civoid ipu_di_put(struct ipu_di *di) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci mutex_lock(&ipu_di_lock); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci di->inuse = false; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci mutex_unlock(&ipu_di_lock); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ipu_di_put); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciint ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, 71262306a36Sopenharmony_ci unsigned long base, 71362306a36Sopenharmony_ci u32 module, struct clk *clk_ipu) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct ipu_di *di; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (id > 1) 71862306a36Sopenharmony_ci return -ENODEV; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 72162306a36Sopenharmony_ci if (!di) 72262306a36Sopenharmony_ci return -ENOMEM; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ipu->di_priv[id] = di; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); 72762306a36Sopenharmony_ci if (IS_ERR(di->clk_di)) 72862306a36Sopenharmony_ci return PTR_ERR(di->clk_di); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci di->module = module; 73162306a36Sopenharmony_ci di->id = id; 73262306a36Sopenharmony_ci di->clk_ipu = clk_ipu; 73362306a36Sopenharmony_ci di->base = devm_ioremap(dev, base, PAGE_SIZE); 73462306a36Sopenharmony_ci if (!di->base) 73562306a36Sopenharmony_ci return -ENOMEM; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ipu_di_write(di, 0x10, DI_BS_CLKGEN0); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", 74062306a36Sopenharmony_ci id, base, di->base); 74162306a36Sopenharmony_ci di->inuse = false; 74262306a36Sopenharmony_ci di->ipu = ipu; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_civoid ipu_di_exit(struct ipu_soc *ipu, int id) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci} 750