Commit e58e0ea5935cc4456f8f6f92763fdbd20ba750f1
1 parent
a2fd185001
Exists in
smarc_8mq-imx_v2020.04_5.4.24_2.1.0
and in
1 other branch
MLK-23964-16 video: Add video drivers for iMX8MM/iMX8MN display
Update mxsfb for LCD video driver, add mipi dsi driver, ADV7535 and raydium-rm67191 panel drivers. Signed-off-by: Ye Li <ye.li@nxp.com> (cherry picked from commit 161c5785d2b8b3046bd673037d070fdbda61fb7c)
Showing 9 changed files with 2180 additions and 43 deletions Side-by-side Diff
drivers/video/Kconfig
... | ... | @@ -346,6 +346,15 @@ |
346 | 346 | Say Y here if you want to enable support for Raydium RM68200 |
347 | 347 | 720x1280 DSI video mode panel. |
348 | 348 | |
349 | +config VIDEO_LCD_RAYDIUM_RM67191 | |
350 | + bool "RM67191 DSI LCD panel support" | |
351 | + depends on DM_VIDEO | |
352 | + select VIDEO_MIPI_DSI | |
353 | + default n | |
354 | + help | |
355 | + Say Y here if you want to enable support for Raydium RM68200 | |
356 | + 1080x1920 DSI video mode panel. | |
357 | + | |
349 | 358 | config VIDEO_LCD_SSD2828 |
350 | 359 | bool "SSD2828 bridge chip" |
351 | 360 | default n |
... | ... | @@ -793,6 +802,15 @@ |
793 | 802 | help |
794 | 803 | Say Y here if you want to enable support for ITE IT6263 |
795 | 804 | LVDS to HDMI connector, currently only support 1280x720P. |
805 | + | |
806 | +config VIDEO_ADV7535 | |
807 | + bool "ADV7535 MIPI DSI to HDMI connector" | |
808 | + depends on DM_VIDEO | |
809 | + select VIDEO_MIPI_DSI | |
810 | + default n | |
811 | + help | |
812 | + Say Y here if you want to enable support for ADI ADV7535 | |
813 | + DSI to HDMI connector, currently only support 1920x1080. | |
796 | 814 | |
797 | 815 | endmenu |
drivers/video/Makefile
... | ... | @@ -54,7 +54,9 @@ |
54 | 54 | obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o |
55 | 55 | obj-$(CONFIG_VIDEO_LCD_ORISETECH_OTM8009A) += orisetech_otm8009a.o |
56 | 56 | obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o |
57 | +obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM67191) += raydium-rm67191.o | |
57 | 58 | obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o |
59 | +obj-$(CONFIG_VIDEO_ADV7535) += adv7535.o | |
58 | 60 | obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o |
59 | 61 | obj-${CONFIG_VIDEO_MESON} += meson/ |
60 | 62 | obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o |
drivers/video/adv7535.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright 2019 NXP | |
4 | + * | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm.h> | |
9 | +#include <mipi_dsi.h> | |
10 | +#include <panel.h> | |
11 | +#include <asm/gpio.h> | |
12 | +#include <i2c.h> | |
13 | +#include <linux/err.h> | |
14 | + | |
15 | +struct adv7535_priv { | |
16 | + unsigned int addr; | |
17 | + unsigned int addr_cec; | |
18 | + unsigned int lanes; | |
19 | + enum mipi_dsi_pixel_format format; | |
20 | + unsigned long mode_flags; | |
21 | + struct udevice *cec_dev; | |
22 | +}; | |
23 | + | |
24 | +static const struct display_timing default_timing = { | |
25 | + .pixelclock.typ = 148500000, | |
26 | + .hactive.typ = 1920, | |
27 | + .hfront_porch.typ = 88, | |
28 | + .hback_porch.typ = 148, | |
29 | + .hsync_len.typ = 44, | |
30 | + .vactive.typ = 1080, | |
31 | + .vfront_porch.typ = 4, | |
32 | + .vback_porch.typ = 36, | |
33 | + .vsync_len.typ = 5, | |
34 | +}; | |
35 | + | |
36 | +static int adv7535_i2c_reg_write(struct udevice *dev, uint addr, uint mask, uint data) | |
37 | +{ | |
38 | + uint8_t valb; | |
39 | + int err; | |
40 | + | |
41 | + if (mask != 0xff) { | |
42 | + err = dm_i2c_read(dev, addr, &valb, 1); | |
43 | + if (err) | |
44 | + return err; | |
45 | + | |
46 | + valb &= ~mask; | |
47 | + valb |= data; | |
48 | + } else { | |
49 | + valb = data; | |
50 | + } | |
51 | + | |
52 | + err = dm_i2c_write(dev, addr, &valb, 1); | |
53 | + return err; | |
54 | +} | |
55 | + | |
56 | +static int adv7535_i2c_reg_read(struct udevice *dev, uint8_t addr, uint8_t *data) | |
57 | +{ | |
58 | + uint8_t valb; | |
59 | + int err; | |
60 | + | |
61 | + err = dm_i2c_read(dev, addr, &valb, 1); | |
62 | + if (err) | |
63 | + return err; | |
64 | + | |
65 | + *data = (int)valb; | |
66 | + return 0; | |
67 | +} | |
68 | + | |
69 | +static int adv7535_enable(struct udevice *dev) | |
70 | +{ | |
71 | + struct adv7535_priv *priv = dev_get_priv(dev); | |
72 | + uint8_t val; | |
73 | + | |
74 | + adv7535_i2c_reg_read(dev, 0x00, &val); | |
75 | + debug("Chip revision: 0x%x (expected: 0x14)\n", val); | |
76 | + adv7535_i2c_reg_read(priv->cec_dev, 0x00, &val); | |
77 | + debug("Chip ID MSB: 0x%x (expected: 0x75)\n", val); | |
78 | + adv7535_i2c_reg_read(priv->cec_dev, 0x01, &val); | |
79 | + debug("Chip ID LSB: 0x%x (expected: 0x33)\n", val); | |
80 | + | |
81 | + /* Power */ | |
82 | + adv7535_i2c_reg_write(dev, 0x41, 0xff, 0x10); | |
83 | + /* Initialisation (Fixed) Registers */ | |
84 | + adv7535_i2c_reg_write(dev, 0x16, 0xff, 0x20); | |
85 | + adv7535_i2c_reg_write(dev, 0x9A, 0xff, 0xE0); | |
86 | + adv7535_i2c_reg_write(dev, 0xBA, 0xff, 0x70); | |
87 | + adv7535_i2c_reg_write(dev, 0xDE, 0xff, 0x82); | |
88 | + adv7535_i2c_reg_write(dev, 0xE4, 0xff, 0x40); | |
89 | + adv7535_i2c_reg_write(dev, 0xE5, 0xff, 0x80); | |
90 | + adv7535_i2c_reg_write(priv->cec_dev, 0x15, 0xff, 0xD0); | |
91 | + adv7535_i2c_reg_write(priv->cec_dev, 0x17, 0xff, 0xD0); | |
92 | + adv7535_i2c_reg_write(priv->cec_dev, 0x24, 0xff, 0x20); | |
93 | + adv7535_i2c_reg_write(priv->cec_dev, 0x57, 0xff, 0x11); | |
94 | + adv7535_i2c_reg_write(priv->cec_dev, 0x05, 0xff, 0xc8); | |
95 | + | |
96 | + /* 4 x DSI Lanes */ | |
97 | + adv7535_i2c_reg_write(priv->cec_dev, 0x1C, 0xff, 0x40); | |
98 | + | |
99 | + /* DSI Pixel Clock Divider */ | |
100 | + //adv7535_i2c_reg_write(priv->cec_dev, 0x16, 0xff, 0x0); | |
101 | + adv7535_i2c_reg_write(priv->cec_dev, 0x16, 0xff, 0x18); | |
102 | + | |
103 | + /* Enable Internal Timing Generator */ | |
104 | + adv7535_i2c_reg_write(priv->cec_dev, 0x27, 0xff, 0xCB); | |
105 | + /* 1920 x 1080p 60Hz */ | |
106 | + adv7535_i2c_reg_write(priv->cec_dev, 0x28, 0xff, 0x89); /* total width */ | |
107 | + adv7535_i2c_reg_write(priv->cec_dev, 0x29, 0xff, 0x80); /* total width */ | |
108 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2A, 0xff, 0x02); /* hsync */ | |
109 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2B, 0xff, 0xC0); /* hsync */ | |
110 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2C, 0xff, 0x05); /* hfp */ | |
111 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2D, 0xff, 0x80); /* hfp */ | |
112 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2E, 0xff, 0x09); /* hbp */ | |
113 | + adv7535_i2c_reg_write(priv->cec_dev, 0x2F, 0xff, 0x40); /* hbp */ | |
114 | + | |
115 | + adv7535_i2c_reg_write(priv->cec_dev, 0x30, 0xff, 0x46); /* total height */ | |
116 | + adv7535_i2c_reg_write(priv->cec_dev, 0x31, 0xff, 0x50); /* total height */ | |
117 | + adv7535_i2c_reg_write(priv->cec_dev, 0x32, 0xff, 0x00); /* vsync */ | |
118 | + adv7535_i2c_reg_write(priv->cec_dev, 0x33, 0xff, 0x50); /* vsync */ | |
119 | + adv7535_i2c_reg_write(priv->cec_dev, 0x34, 0xff, 0x00); /* vfp */ | |
120 | + adv7535_i2c_reg_write(priv->cec_dev, 0x35, 0xff, 0x40); /* vfp */ | |
121 | + adv7535_i2c_reg_write(priv->cec_dev, 0x36, 0xff, 0x02); /* vbp */ | |
122 | + adv7535_i2c_reg_write(priv->cec_dev, 0x37, 0xff, 0x40); /* vbp */ | |
123 | + | |
124 | + /* Reset Internal Timing Generator */ | |
125 | + adv7535_i2c_reg_write(priv->cec_dev, 0x27, 0xff, 0xCB); | |
126 | + adv7535_i2c_reg_write(priv->cec_dev, 0x27, 0xff, 0x8B); | |
127 | + adv7535_i2c_reg_write(priv->cec_dev, 0x27, 0xff, 0xCB); | |
128 | + | |
129 | + /* HDMI Output */ | |
130 | + adv7535_i2c_reg_write(dev, 0xAF, 0xff, 0x16); | |
131 | + /* AVI Infoframe - RGB - 16-9 Aspect Ratio */ | |
132 | + adv7535_i2c_reg_write(dev, 0x55, 0xff, 0x10); | |
133 | + //adv7535_i2c_reg_write(dev, 0x55, 0xff, 0x02); | |
134 | + adv7535_i2c_reg_write(dev, 0x56, 0xff, 0x28); | |
135 | + //adv7535_i2c_reg_write(dev, 0x56, 0xff, 0x0); | |
136 | + | |
137 | + /* GC Packet Enable */ | |
138 | + adv7535_i2c_reg_write(dev, 0x40, 0xff, 0x80); | |
139 | + //adv7535_i2c_reg_write(dev, 0x40, 0xff, 0x0); | |
140 | + /* GC Colour Depth - 24 Bit */ | |
141 | + adv7535_i2c_reg_write(dev, 0x4C, 0xff, 0x04); | |
142 | + //adv7535_i2c_reg_write(dev, 0x4C, 0xff, 0x0); | |
143 | + /* Down Dither Output Colour Depth - 8 Bit (default) */ | |
144 | + adv7535_i2c_reg_write(dev, 0x49, 0xff, 0x00); | |
145 | + | |
146 | + /* set low refresh 1080p30 */ | |
147 | + adv7535_i2c_reg_write(dev, 0x4A, 0xff, 0x80); /*should be 0x80 for 1080p60 and 0x8c for 1080p30*/ | |
148 | + | |
149 | + /* HDMI Output Enable */ | |
150 | + //adv7535_i2c_reg_write(priv->cec_dev, 0xbe, 0xff, 0x3c); | |
151 | + adv7535_i2c_reg_write(priv->cec_dev, 0xbe, 0xff, 0x3d); | |
152 | + adv7535_i2c_reg_write(priv->cec_dev, 0x03, 0xff, 0x89); | |
153 | + | |
154 | + return 0; | |
155 | +} | |
156 | + | |
157 | +static int adv7535_enable_backlight(struct udevice *dev) | |
158 | +{ | |
159 | + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); | |
160 | + struct mipi_dsi_device *device = plat->device; | |
161 | + int ret; | |
162 | + | |
163 | + ret = mipi_dsi_attach(device); | |
164 | + if (ret < 0) | |
165 | + return ret; | |
166 | + | |
167 | + return 0; | |
168 | +} | |
169 | + | |
170 | +static int adv7535_get_display_timing(struct udevice *dev, | |
171 | + struct display_timing *timings) | |
172 | +{ | |
173 | + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); | |
174 | + struct mipi_dsi_device *device = plat->device; | |
175 | + struct adv7535_priv *priv = dev_get_priv(dev); | |
176 | + | |
177 | + memcpy(timings, &default_timing, sizeof(*timings)); | |
178 | + | |
179 | + /* fill characteristics of DSI data link */ | |
180 | + if (device) { | |
181 | + device->lanes = priv->lanes; | |
182 | + device->format = priv->format; | |
183 | + device->mode_flags = priv->mode_flags; | |
184 | + } | |
185 | + | |
186 | + return 0; | |
187 | +} | |
188 | + | |
189 | +static int adv7535_probe(struct udevice *dev) | |
190 | +{ | |
191 | + struct adv7535_priv *priv = dev_get_priv(dev); | |
192 | + int ret; | |
193 | + | |
194 | + debug("%s\n", __func__); | |
195 | + | |
196 | + priv->format = MIPI_DSI_FMT_RGB888; | |
197 | + priv->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | | |
198 | + MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE, | |
199 | + | |
200 | + priv->addr = dev_read_addr(dev); | |
201 | + if (priv->addr == 0) | |
202 | + return -ENODEV; | |
203 | + | |
204 | + ret = dev_read_u32(dev, "adi,dsi-lanes", &priv->lanes); | |
205 | + if (ret) { | |
206 | + dev_err(dev, "Failed to get dsi-lanes property (%d)\n", ret); | |
207 | + return ret; | |
208 | + } | |
209 | + | |
210 | + if (priv->lanes < 1 || priv->lanes > 4) { | |
211 | + dev_err(dev, "Invalid dsi-lanes: %d\n", priv->lanes); | |
212 | + return -EINVAL; | |
213 | + } | |
214 | + | |
215 | + ret = dev_read_u32(dev, "adi,addr-cec", &priv->addr_cec); | |
216 | + if (ret) { | |
217 | + dev_err(dev, "Failed to get addr-cec property (%d)\n", ret); | |
218 | + return -EINVAL; | |
219 | + } | |
220 | + | |
221 | + ret = dm_i2c_probe(dev_get_parent(dev), priv->addr_cec, 0, &priv->cec_dev); | |
222 | + if (ret) { | |
223 | + dev_err(dev, "Can't find cec device id=0x%x\n", priv->addr_cec); | |
224 | + return -ENODEV; | |
225 | + } | |
226 | + | |
227 | + adv7535_enable(dev); | |
228 | + | |
229 | + return 0; | |
230 | +} | |
231 | + | |
232 | +static const struct panel_ops adv7535_ops = { | |
233 | + .enable_backlight = adv7535_enable_backlight, | |
234 | + .get_display_timing = adv7535_get_display_timing, | |
235 | +}; | |
236 | + | |
237 | +static const struct udevice_id adv7535_ids[] = { | |
238 | + { .compatible = "adi,adv7533" }, | |
239 | + { } | |
240 | +}; | |
241 | + | |
242 | +U_BOOT_DRIVER(adv7535_mipi2hdmi) = { | |
243 | + .name = "adv7535_mipi2hdmi", | |
244 | + .id = UCLASS_PANEL, | |
245 | + .of_match = adv7535_ids, | |
246 | + .ops = &adv7535_ops, | |
247 | + .probe = adv7535_probe, | |
248 | + .platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat), | |
249 | + .priv_auto_alloc_size = sizeof(struct adv7535_priv), | |
250 | +}; |
drivers/video/imx/Kconfig
... | ... | @@ -48,4 +48,23 @@ |
48 | 48 | select VIDEO_LINK |
49 | 49 | help |
50 | 50 | Support for HDMI on i.MX8MQ processors. |
51 | + | |
52 | +config VIDEO_SEC_MIPI_DSI | |
53 | + bool | |
54 | + select VIDEO_MIPI_DSI | |
55 | + help | |
56 | + Enables the common driver code for the Samsung | |
57 | + MIPI DSI block found in SoCs from various vendors. | |
58 | + As this does not provide any functionality by itself (but | |
59 | + rather requires a SoC-specific glue driver to call it), it | |
60 | + can not be enabled from the configuration menu. | |
61 | + | |
62 | +config VIDEO_IMX_SEC_DSI | |
63 | + bool "Enable IMX SEC DSI video support" | |
64 | + select VIDEO_BRIDGE | |
65 | + select VIDEO_SEC_MIPI_DSI | |
66 | + select VIDEO_LINK | |
67 | + help | |
68 | + This option enables support DSI internal bridge which can be used on | |
69 | + devices which have DSI devices connected. |
drivers/video/imx/Makefile
... | ... | @@ -7,5 +7,7 @@ |
7 | 7 | obj-$(CONFIG_VIDEO_IMXDPUV1) += imxdpuv1.o imx8_dc.o |
8 | 8 | obj-$(CONFIG_VIDEO_IMX8_LVDS) += imx8_lvds.o |
9 | 9 | obj-$(CONFIG_VIDEO_IMX8M_DCSS) += imx8m_dcss.o |
10 | +obj-$(CONFIG_VIDEO_SEC_MIPI_DSI) += sec_mipi_dsim.o | |
11 | +obj-$(CONFIG_VIDEO_IMX_SEC_DSI) += sec_dsim_imx.o | |
10 | 12 | obj-y += hdmi/ |
drivers/video/imx/sec_dsim_imx.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright 2019 NXP | |
4 | + * | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <clk.h> | |
9 | +#include <dm.h> | |
10 | +#include <dsi_host.h> | |
11 | +#include <mipi_dsi.h> | |
12 | +#include <panel.h> | |
13 | +#include <reset.h> | |
14 | +#include <video.h> | |
15 | +#include <video_bridge.h> | |
16 | +#include <video_link.h> | |
17 | +#include <asm/io.h> | |
18 | +#include <asm/arch/gpio.h> | |
19 | +#include <dm/device-internal.h> | |
20 | +#include <linux/iopoll.h> | |
21 | +#include <linux/err.h> | |
22 | +#include <power/regulator.h> | |
23 | +#include <regmap.h> | |
24 | +#include <syscon.h> | |
25 | + | |
26 | +/* fixed phy ref clk rate */ | |
27 | +#define PHY_REF_CLK 27000000 | |
28 | + | |
29 | +struct imx_sec_dsim_priv { | |
30 | + struct mipi_dsi_device device; | |
31 | + void __iomem *base; | |
32 | + struct udevice *panel; | |
33 | + struct udevice *dsi_host; | |
34 | + struct reset_ctl_bulk soft_resetn; | |
35 | + struct reset_ctl_bulk clk_enable; | |
36 | + struct reset_ctl_bulk mipi_reset; | |
37 | +}; | |
38 | + | |
39 | +static int sec_dsim_rstc_reset(struct reset_ctl_bulk *rstc, bool assert) | |
40 | +{ | |
41 | + int ret; | |
42 | + | |
43 | + if (!rstc) | |
44 | + return 0; | |
45 | + | |
46 | + ret = assert ? reset_assert_bulk(rstc) : | |
47 | + reset_deassert_bulk(rstc); | |
48 | + | |
49 | + return ret; | |
50 | +} | |
51 | + | |
52 | +static int sec_dsim_of_parse_resets(struct udevice *dev) | |
53 | +{ | |
54 | + int ret; | |
55 | + ofnode parent, child; | |
56 | + struct ofnode_phandle_args args; | |
57 | + struct reset_ctl_bulk rstc; | |
58 | + const char *compat; | |
59 | + uint32_t rstc_num = 0; | |
60 | + | |
61 | + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); | |
62 | + | |
63 | + ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, | |
64 | + 0, &args); | |
65 | + if (ret) | |
66 | + return ret; | |
67 | + | |
68 | + parent = args.node; | |
69 | + ofnode_for_each_subnode(child, parent) { | |
70 | + compat = ofnode_get_property(child, "compatible", NULL); | |
71 | + if (!compat) | |
72 | + continue; | |
73 | + | |
74 | + ret = reset_get_bulk_nodev(child, &rstc); | |
75 | + if (ret) | |
76 | + continue; | |
77 | + | |
78 | + if (!of_compat_cmp("dsi,soft-resetn", compat, 0)) { | |
79 | + priv->soft_resetn = rstc; | |
80 | + rstc_num++; | |
81 | + } else if (!of_compat_cmp("dsi,clk-enable", compat, 0)) { | |
82 | + priv->clk_enable = rstc; | |
83 | + rstc_num++; | |
84 | + } else if (!of_compat_cmp("dsi,mipi-reset", compat, 0)) { | |
85 | + priv->mipi_reset = rstc; | |
86 | + rstc_num++; | |
87 | + } else | |
88 | + dev_warn(dev, "invalid dsim reset node: %s\n", compat); | |
89 | + } | |
90 | + | |
91 | + if (!rstc_num) { | |
92 | + dev_err(dev, "no invalid reset control exists\n"); | |
93 | + return -EINVAL; | |
94 | + } | |
95 | + | |
96 | + return 0; | |
97 | +} | |
98 | + | |
99 | +static int imx_sec_dsim_attach(struct udevice *dev) | |
100 | +{ | |
101 | + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); | |
102 | + struct mipi_dsi_device *device = &priv->device; | |
103 | + struct mipi_dsi_panel_plat *mplat; | |
104 | + struct display_timing timings; | |
105 | + int ret; | |
106 | + | |
107 | + priv->panel = video_link_get_next_device(dev); | |
108 | + if (!priv->panel || | |
109 | + device_get_uclass_id(priv->panel) != UCLASS_PANEL) { | |
110 | + dev_err(dev, "get panel device error\n"); | |
111 | + return -ENODEV; | |
112 | + } | |
113 | + | |
114 | + mplat = dev_get_platdata(priv->panel); | |
115 | + mplat->device = &priv->device; | |
116 | + | |
117 | + ret = video_link_get_display_timings(&timings); | |
118 | + if (ret) { | |
119 | + dev_err(dev, "decode display timing error %d\n", ret); | |
120 | + return ret; | |
121 | + } | |
122 | + | |
123 | + ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host); | |
124 | + if (ret) { | |
125 | + dev_err(dev, "No video dsi host detected %d\n", ret); | |
126 | + return ret; | |
127 | + } | |
128 | + | |
129 | + ret = dsi_host_init(priv->dsi_host, device, &timings, 4, | |
130 | + NULL); | |
131 | + if (ret) { | |
132 | + dev_err(dev, "failed to initialize mipi dsi host\n"); | |
133 | + return ret; | |
134 | + } | |
135 | + | |
136 | + return 0; | |
137 | +} | |
138 | + | |
139 | +static int imx_sec_dsim_set_backlight(struct udevice *dev, int percent) | |
140 | +{ | |
141 | + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); | |
142 | + int ret; | |
143 | + | |
144 | + ret = panel_enable_backlight(priv->panel); | |
145 | + if (ret) { | |
146 | + dev_err(dev, "panel %s enable backlight error %d\n", | |
147 | + priv->panel->name, ret); | |
148 | + return ret; | |
149 | + } | |
150 | + | |
151 | + ret = dsi_host_enable(priv->dsi_host); | |
152 | + if (ret) { | |
153 | + dev_err(dev, "failed to enable mipi dsi host\n"); | |
154 | + return ret; | |
155 | + } | |
156 | + | |
157 | + return 0; | |
158 | +} | |
159 | + | |
160 | +static int imx_sec_dsim_probe(struct udevice *dev) | |
161 | +{ | |
162 | + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); | |
163 | + struct mipi_dsi_device *device = &priv->device; | |
164 | + int ret; | |
165 | + | |
166 | + device->dev = dev; | |
167 | + | |
168 | + sec_dsim_of_parse_resets(dev); | |
169 | + | |
170 | + ret = sec_dsim_rstc_reset(&priv->soft_resetn, false); | |
171 | + if (ret) { | |
172 | + dev_err(dev, "deassert soft_resetn failed\n"); | |
173 | + return ret; | |
174 | + } | |
175 | + | |
176 | + ret = sec_dsim_rstc_reset(&priv->clk_enable, true); | |
177 | + if (ret) { | |
178 | + dev_err(dev, "assert clk_enable failed\n"); | |
179 | + return ret; | |
180 | + } | |
181 | + | |
182 | + ret = sec_dsim_rstc_reset(&priv->mipi_reset, false); | |
183 | + if (ret) { | |
184 | + dev_err(dev, "deassert mipi_reset failed\n"); | |
185 | + return ret; | |
186 | + } | |
187 | + | |
188 | + return ret; | |
189 | +} | |
190 | + | |
191 | +static int imx_sec_dsim_remove(struct udevice *dev) | |
192 | +{ | |
193 | + struct imx_sec_dsim_priv *priv = dev_get_priv(dev); | |
194 | + int ret; | |
195 | + | |
196 | + if (priv->panel) | |
197 | + device_remove(priv->panel, DM_REMOVE_NORMAL); | |
198 | + | |
199 | + ret = dsi_host_disable(priv->dsi_host); | |
200 | + if (ret) { | |
201 | + dev_err(dev, "failed to enable mipi dsi host\n"); | |
202 | + return ret; | |
203 | + } | |
204 | + | |
205 | + return 0; | |
206 | +} | |
207 | + | |
208 | +struct video_bridge_ops imx_sec_dsim_ops = { | |
209 | + .attach = imx_sec_dsim_attach, | |
210 | + .set_backlight = imx_sec_dsim_set_backlight, | |
211 | +}; | |
212 | + | |
213 | +static const struct udevice_id imx_sec_dsim_ids[] = { | |
214 | + { .compatible = "fsl,imx8mm-mipi-dsim" }, | |
215 | + { .compatible = "fsl,imx8mn-mipi-dsim" }, | |
216 | + { } | |
217 | +}; | |
218 | + | |
219 | +U_BOOT_DRIVER(imx_sec_dsim) = { | |
220 | + .name = "imx_sec_dsim", | |
221 | + .id = UCLASS_VIDEO_BRIDGE, | |
222 | + .of_match = imx_sec_dsim_ids, | |
223 | + .bind = dm_scan_fdt_dev, | |
224 | + .remove = imx_sec_dsim_remove, | |
225 | + .probe = imx_sec_dsim_probe, | |
226 | + .ops = &imx_sec_dsim_ops, | |
227 | + .priv_auto_alloc_size = sizeof(struct imx_sec_dsim_priv), | |
228 | +}; |
drivers/video/imx/sec_mipi_dsim.c
Changes suppressed. Click to show
1 | +/* | |
2 | + * Copyright 2018 NXP | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm.h> | |
9 | +#include <asm/io.h> | |
10 | +#include <linux/err.h> | |
11 | +#include <asm/unaligned.h> | |
12 | +#include <asm/arch/clock.h> | |
13 | +#include <asm/arch/imx-regs.h> | |
14 | +#include <asm/arch/sys_proto.h> | |
15 | +#include <div64.h> | |
16 | +#include <video_bridge.h> | |
17 | +#include <panel.h> | |
18 | +#include <dsi_host.h> | |
19 | +#include <asm/arch/gpio.h> | |
20 | +#include <dm/device-internal.h> | |
21 | + | |
22 | +#define MIPI_FIFO_TIMEOUT 250000 /* 250ms */ | |
23 | + | |
24 | +#define DRIVER_NAME "sec_mipi_dsim" | |
25 | + | |
26 | +/* dsim registers */ | |
27 | +#define DSIM_VERSION 0x00 | |
28 | +#define DSIM_STATUS 0x04 | |
29 | +#define DSIM_RGB_STATUS 0x08 | |
30 | +#define DSIM_SWRST 0x0c | |
31 | +#define DSIM_CLKCTRL 0x10 | |
32 | +#define DSIM_TIMEOUT 0x14 | |
33 | +#define DSIM_CONFIG 0x18 | |
34 | +#define DSIM_ESCMODE 0x1c | |
35 | +#define DSIM_MDRESOL 0x20 | |
36 | +#define DSIM_MVPORCH 0x24 | |
37 | +#define DSIM_MHPORCH 0x28 | |
38 | +#define DSIM_MSYNC 0x2c | |
39 | +#define DSIM_SDRESOL 0x30 | |
40 | +#define DSIM_INTSRC 0x34 | |
41 | +#define DSIM_INTMSK 0x38 | |
42 | + | |
43 | +/* packet */ | |
44 | +#define DSIM_PKTHDR 0x3c | |
45 | +#define DSIM_PAYLOAD 0x40 | |
46 | +#define DSIM_RXFIFO 0x44 | |
47 | +#define DSIM_FIFOTHLD 0x48 | |
48 | +#define DSIM_FIFOCTRL 0x4c | |
49 | +#define DSIM_MEMACCHR 0x50 | |
50 | +#define DSIM_MULTI_PKT 0x78 | |
51 | + | |
52 | +/* pll control */ | |
53 | +#define DSIM_PLLCTRL_1G 0x90 | |
54 | +#define DSIM_PLLCTRL 0x94 | |
55 | +#define DSIM_PLLCTRL1 0x98 | |
56 | +#define DSIM_PLLCTRL2 0x9c | |
57 | +#define DSIM_PLLTMR 0xa0 | |
58 | + | |
59 | +/* dphy */ | |
60 | +#define DSIM_PHYTIMING 0xb4 | |
61 | +#define DSIM_PHYTIMING1 0xb8 | |
62 | +#define DSIM_PHYTIMING2 0xbc | |
63 | + | |
64 | +/* reg bit manipulation */ | |
65 | +#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s)) | |
66 | +#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s)) | |
67 | +#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s)) | |
68 | + | |
69 | +/* register bit fields */ | |
70 | +#define STATUS_PLLSTABLE BIT(31) | |
71 | +#define STATUS_SWRSTRLS BIT(20) | |
72 | +#define STATUS_TXREADYHSCLK BIT(10) | |
73 | +#define STATUS_ULPSCLK BIT(9) | |
74 | +#define STATUS_STOPSTATECLK BIT(8) | |
75 | +#define STATUS_GET_ULPSDAT(x) REG_GET(x, 7, 4) | |
76 | +#define STATUS_GET_STOPSTATEDAT(x) REG_GET(x, 3, 0) | |
77 | + | |
78 | +#define RGB_STATUS_CMDMODE_INSEL BIT(31) | |
79 | +#define RGB_STATUS_GET_RGBSTATE(x) REG_GET(x, 12, 0) | |
80 | + | |
81 | +#define CLKCTRL_TXREQUESTHSCLK BIT(31) | |
82 | +#define CLKCTRL_DPHY_SEL_1G BIT(29) | |
83 | +#define CLKCTRL_DPHY_SEL_1P5G (0x0 << 29) | |
84 | +#define CLKCTRL_ESCCLKEN BIT(28) | |
85 | +#define CLKCTRL_PLLBYPASS BIT(29) | |
86 | +#define CLKCTRL_BYTECLKSRC_DPHY_PLL REG_PUT(0, 26, 25) | |
87 | +#define CLKCTRL_BYTECLKEN BIT(24) | |
88 | +#define CLKCTRL_SET_LANEESCCLKEN(x) REG_PUT(x, 23, 19) | |
89 | +#define CLKCTRL_SET_ESCPRESCALER(x) REG_PUT(x, 15, 0) | |
90 | + | |
91 | +#define TIMEOUT_SET_BTAOUT(x) REG_PUT(x, 23, 16) | |
92 | +#define TIMEOUT_SET_LPDRTOUT(x) REG_PUT(x, 15, 0) | |
93 | + | |
94 | +#define CONFIG_NON_CONTINOUS_CLOCK_LANE BIT(31) | |
95 | +#define CONFIG_CLKLANE_STOP_START BIT(30) | |
96 | +#define CONFIG_MFLUSH_VS BIT(29) | |
97 | +#define CONFIG_EOT_R03 BIT(28) | |
98 | +#define CONFIG_SYNCINFORM BIT(27) | |
99 | +#define CONFIG_BURSTMODE BIT(26) | |
100 | +#define CONFIG_VIDEOMODE BIT(25) | |
101 | +#define CONFIG_AUTOMODE BIT(24) | |
102 | +#define CONFIG_HSEDISABLEMODE BIT(23) | |
103 | +#define CONFIG_HFPDISABLEMODE BIT(22) | |
104 | +#define CONFIG_HBPDISABLEMODE BIT(21) | |
105 | +#define CONFIG_HSADISABLEMODE BIT(20) | |
106 | +#define CONFIG_SET_MAINVC(x) REG_PUT(x, 19, 18) | |
107 | +#define CONFIG_SET_SUBVC(x) REG_PUT(x, 17, 16) | |
108 | +#define CONFIG_SET_MAINPIXFORMAT(x) REG_PUT(x, 14, 12) | |
109 | +#define CONFIG_SET_SUBPIXFORMAT(x) REG_PUT(x, 10, 8) | |
110 | +#define CONFIG_SET_NUMOFDATLANE(x) REG_PUT(x, 6, 5) | |
111 | +#define CONFIG_SET_LANEEN(x) REG_PUT(x, 4, 0) | |
112 | + | |
113 | +#define ESCMODE_SET_STOPSTATE_CNT(X) REG_PUT(x, 31, 21) | |
114 | +#define ESCMODE_FORCESTOPSTATE BIT(20) | |
115 | +#define ESCMODE_FORCEBTA BIT(16) | |
116 | +#define ESCMODE_CMDLPDT BIT(7) | |
117 | +#define ESCMODE_TXLPDT BIT(6) | |
118 | +#define ESCMODE_TXTRIGGERRST BIT(5) | |
119 | + | |
120 | +#define MDRESOL_MAINSTANDBY BIT(31) | |
121 | +#define MDRESOL_SET_MAINVRESOL(x) REG_PUT(x, 27, 16) | |
122 | +#define MDRESOL_SET_MAINHRESOL(x) REG_PUT(x, 11, 0) | |
123 | + | |
124 | +#define MVPORCH_SET_CMDALLOW(x) REG_PUT(x, 31, 28) | |
125 | +#define MVPORCH_SET_STABLEVFP(x) REG_PUT(x, 26, 16) | |
126 | +#define MVPORCH_SET_MAINVBP(x) REG_PUT(x, 10, 0) | |
127 | + | |
128 | +#define MHPORCH_SET_MAINHFP(x) REG_PUT(x, 31, 16) | |
129 | +#define MHPORCH_SET_MAINHBP(x) REG_PUT(x, 15, 0) | |
130 | + | |
131 | +#define MSYNC_SET_MAINVSA(x) REG_PUT(x, 31, 22) | |
132 | +#define MSYNC_SET_MAINHSA(x) REG_PUT(x, 15, 0) | |
133 | + | |
134 | +#define INTSRC_PLLSTABLE BIT(31) | |
135 | +#define INTSRC_SWRSTRELEASE BIT(30) | |
136 | +#define INTSRC_SFRPLFIFOEMPTY BIT(29) | |
137 | +#define INTSRC_SFRPHFIFOEMPTY BIT(28) | |
138 | +#define INTSRC_FRAMEDONE BIT(24) | |
139 | +#define INTSRC_LPDRTOUT BIT(21) | |
140 | +#define INTSRC_TATOUT BIT(20) | |
141 | +#define INTSRC_RXDATDONE BIT(18) | |
142 | +#define INTSRC_MASK (INTSRC_PLLSTABLE | \ | |
143 | + INTSRC_SWRSTRELEASE | \ | |
144 | + INTSRC_SFRPLFIFOEMPTY | \ | |
145 | + INTSRC_SFRPHFIFOEMPTY | \ | |
146 | + INTSRC_FRAMEDONE | \ | |
147 | + INTSRC_LPDRTOUT | \ | |
148 | + INTSRC_TATOUT | \ | |
149 | + INTSRC_RXDATDONE) | |
150 | + | |
151 | +#define INTMSK_MSKPLLSTABLE BIT(31) | |
152 | +#define INTMSK_MSKSWRELEASE BIT(30) | |
153 | +#define INTMSK_MSKSFRPLFIFOEMPTY BIT(29) | |
154 | +#define INTMSK_MSKSFRPHFIFOEMPTY BIT(28) | |
155 | +#define INTMSK_MSKFRAMEDONE BIT(24) | |
156 | +#define INTMSK_MSKLPDRTOUT BIT(21) | |
157 | +#define INTMSK_MSKTATOUT BIT(20) | |
158 | +#define INTMSK_MSKRXDATDONE BIT(18) | |
159 | + | |
160 | +#define PKTHDR_SET_DATA1(x) REG_PUT(x, 23, 16) | |
161 | +#define PKTHDR_GET_DATA1(x) REG_GET(x, 23, 16) | |
162 | +#define PKTHDR_SET_DATA0(x) REG_PUT(x, 15, 8) | |
163 | +#define PKTHDR_GET_DATA0(x) REG_GET(x, 15, 8) | |
164 | +#define PKTHDR_GET_WC(x) REG_GET(x, 23, 8) | |
165 | +#define PKTHDR_SET_DI(x) REG_PUT(x, 7, 0) | |
166 | +#define PKTHDR_GET_DI(x) REG_GET(x, 7, 0) | |
167 | +#define PKTHDR_SET_DT(x) REG_PUT(x, 5, 0) | |
168 | +#define PKTHDR_GET_DT(x) REG_GET(x, 5, 0) | |
169 | +#define PKTHDR_SET_VC(x) REG_PUT(x, 7, 6) | |
170 | +#define PKTHDR_GET_VC(x) REG_GET(x, 7, 6) | |
171 | + | |
172 | +#define FIFOCTRL_FULLRX BIT(25) | |
173 | +#define FIFOCTRL_EMPTYRX BIT(24) | |
174 | +#define FIFOCTRL_FULLHSFR BIT(23) | |
175 | +#define FIFOCTRL_EMPTYHSFR BIT(22) | |
176 | +#define FIFOCTRL_FULLLSFR BIT(21) | |
177 | +#define FIFOCTRL_EMPTYLSFR BIT(20) | |
178 | +#define FIFOCTRL_FULLHMAIN BIT(11) | |
179 | +#define FIFOCTRL_EMPTYHMAIN BIT(10) | |
180 | +#define FIFOCTRL_FULLLMAIN BIT(9) | |
181 | +#define FIFOCTRL_EMPTYLMAIN BIT(8) | |
182 | +#define FIFOCTRL_NINITRX BIT(4) | |
183 | +#define FIFOCTRL_NINITSFR BIT(3) | |
184 | +#define FIFOCTRL_NINITI80 BIT(2) | |
185 | +#define FIFOCTRL_NINITSUB BIT(1) | |
186 | +#define FIFOCTRL_NINITMAIN BIT(0) | |
187 | + | |
188 | +#define PLLCTRL_DPDNSWAP_CLK BIT(25) | |
189 | +#define PLLCTRL_DPDNSWAP_DAT BIT(24) | |
190 | +#define PLLCTRL_PLLEN BIT(23) | |
191 | +#define PLLCTRL_SET_PMS(x) REG_PUT(x, 19, 1) | |
192 | + | |
193 | +#define PHYTIMING_SET_M_TLPXCTL(x) REG_PUT(x, 15, 8) | |
194 | +#define PHYTIMING_SET_M_THSEXITCTL(x) REG_PUT(x, 7, 0) | |
195 | + | |
196 | +#define PHYTIMING1_SET_M_TCLKPRPRCTL(x) REG_PUT(x, 31, 24) | |
197 | +#define PHYTIMING1_SET_M_TCLKZEROCTL(x) REG_PUT(x, 23, 16) | |
198 | +#define PHYTIMING1_SET_M_TCLKPOSTCTL(x) REG_PUT(x, 15, 8) | |
199 | +#define PHYTIMING1_SET_M_TCLKTRAILCTL(x) REG_PUT(x, 7, 0) | |
200 | + | |
201 | +#define PHYTIMING2_SET_M_THSPRPRCTL(x) REG_PUT(x, 23, 16) | |
202 | +#define PHYTIMING2_SET_M_THSZEROCTL(x) REG_PUT(x, 15, 8) | |
203 | +#define PHYTIMING2_SET_M_THSTRAILCTL(x) REG_PUT(x, 7, 0) | |
204 | + | |
205 | +#define dsim_read(dsim, reg) readl(dsim->base + reg) | |
206 | +#define dsim_write(dsim, val, reg) writel(val, dsim->base + reg) | |
207 | + | |
208 | +#define MAX_MAIN_HRESOL 2047 | |
209 | +#define MAX_MAIN_VRESOL 2047 | |
210 | +#define MAX_SUB_HRESOL 1024 | |
211 | +#define MAX_SUB_VRESOL 1024 | |
212 | + | |
213 | +/* in KHZ */ | |
214 | +#define MAX_ESC_CLK_FREQ 20000 | |
215 | + | |
216 | +/* dsim all irqs index */ | |
217 | +#define PLLSTABLE 1 | |
218 | +#define SWRSTRELEASE 2 | |
219 | +#define SFRPLFIFOEMPTY 3 | |
220 | +#define SFRPHFIFOEMPTY 4 | |
221 | +#define SYNCOVERRIDE 5 | |
222 | +#define BUSTURNOVER 6 | |
223 | +#define FRAMEDONE 7 | |
224 | +#define LPDRTOUT 8 | |
225 | +#define TATOUT 9 | |
226 | +#define RXDATDONE 10 | |
227 | +#define RXTE 11 | |
228 | +#define RXACK 12 | |
229 | +#define ERRRXECC 13 | |
230 | +#define ERRRXCRC 14 | |
231 | +#define ERRESC3 15 | |
232 | +#define ERRESC2 16 | |
233 | +#define ERRESC1 17 | |
234 | +#define ERRESC0 18 | |
235 | +#define ERRSYNC3 19 | |
236 | +#define ERRSYNC2 20 | |
237 | +#define ERRSYNC1 21 | |
238 | +#define ERRSYNC0 22 | |
239 | +#define ERRCONTROL3 23 | |
240 | +#define ERRCONTROL2 24 | |
241 | +#define ERRCONTROL1 25 | |
242 | +#define ERRCONTROL0 26 | |
243 | + | |
244 | +/* Dispmix Control & GPR Registers */ | |
245 | +#define DISPLAY_MIX_SFT_RSTN_CSR 0x00 | |
246 | +#ifdef CONFIG_IMX8MN | |
247 | +#define MIPI_DSI_I_PRESETn_SFT_EN BIT(0) | BIT(1) | |
248 | +#else | |
249 | + #define MIPI_DSI_I_PRESETn_SFT_EN BIT(5) | |
250 | +#endif | |
251 | +#define DISPLAY_MIX_CLK_EN_CSR 0x04 | |
252 | + | |
253 | +#ifdef CONFIG_IMX8MN | |
254 | +#define MIPI_DSI_PCLK_SFT_EN BIT(0) | |
255 | +#define MIPI_DSI_CLKREF_SFT_EN BIT(1) | |
256 | +#else | |
257 | + #define MIPI_DSI_PCLK_SFT_EN BIT(8) | |
258 | + #define MIPI_DSI_CLKREF_SFT_EN BIT(9) | |
259 | +#endif | |
260 | +#define GPR_MIPI_RESET_DIV 0x08 | |
261 | + /* Clock & Data lanes reset: Active Low */ | |
262 | + #define GPR_MIPI_S_RESETN BIT(16) | |
263 | + #define GPR_MIPI_M_RESETN BIT(17) | |
264 | + | |
265 | +#define PS2KHZ(ps) (1000000000UL / (ps)) | |
266 | + | |
267 | +#define MIPI_HFP_PKT_OVERHEAD 6 | |
268 | +#define MIPI_HBP_PKT_OVERHEAD 6 | |
269 | +#define MIPI_HSA_PKT_OVERHEAD 6 | |
270 | + | |
271 | + | |
272 | +/* DSIM PLL configuration from spec: | |
273 | + * | |
274 | + * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S) | |
275 | + * Fin_pll = Fin / P (6 ~ 12 MHz) | |
276 | + * S: [2:0], M: [12:3], P: [18:13], so | |
277 | + * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33] | |
278 | + * | |
279 | + */ | |
280 | + | |
281 | +struct sec_mipi_dsim { | |
282 | + void __iomem *base; | |
283 | + | |
284 | + /* kHz clocks */ | |
285 | + uint64_t pix_clk; | |
286 | + uint64_t bit_clk; | |
287 | + | |
288 | + unsigned int lanes; | |
289 | + unsigned int channel; /* virtual channel */ | |
290 | + enum mipi_dsi_pixel_format format; | |
291 | + unsigned long mode_flags; | |
292 | + unsigned int pms; | |
293 | + unsigned int p; | |
294 | + unsigned int m; | |
295 | + unsigned int s; | |
296 | + | |
297 | + struct mipi_dsi_device *device; | |
298 | + uint32_t max_data_lanes; | |
299 | + uint64_t max_data_rate; | |
300 | + | |
301 | + struct mipi_dsi_host dsi_host; | |
302 | + | |
303 | + struct display_timing timings; | |
304 | +}; | |
305 | + | |
306 | +static int sec_mipi_dsim_wait_for_pkt_done(struct sec_mipi_dsim *dsim, unsigned long timeout) | |
307 | +{ | |
308 | + uint32_t intsrc; | |
309 | + | |
310 | + do { | |
311 | + intsrc = dsim_read(dsim, DSIM_INTSRC); | |
312 | + if (intsrc & INTSRC_SFRPLFIFOEMPTY) { | |
313 | + dsim_write(dsim, INTSRC_SFRPLFIFOEMPTY, DSIM_INTSRC); | |
314 | + return 0; | |
315 | + } | |
316 | + | |
317 | + udelay(1); | |
318 | + } while (--timeout); | |
319 | + | |
320 | + return -ETIMEDOUT; | |
321 | +} | |
322 | + | |
323 | +static int sec_mipi_dsim_wait_for_hdr_done(struct sec_mipi_dsim *dsim, unsigned long timeout) | |
324 | +{ | |
325 | + uint32_t intsrc; | |
326 | + | |
327 | + do { | |
328 | + intsrc = dsim_read(dsim, DSIM_INTSRC); | |
329 | + if (intsrc & INTSRC_SFRPHFIFOEMPTY) { | |
330 | + dsim_write(dsim, INTSRC_SFRPHFIFOEMPTY, DSIM_INTSRC); | |
331 | + return 0; | |
332 | + } | |
333 | + | |
334 | + udelay(1); | |
335 | + } while (--timeout); | |
336 | + | |
337 | + return -ETIMEDOUT; | |
338 | +} | |
339 | + | |
340 | + | |
341 | +static int sec_mipi_dsim_wait_for_rx_done(struct sec_mipi_dsim *dsim, unsigned long timeout) | |
342 | +{ | |
343 | + uint32_t intsrc; | |
344 | + | |
345 | + do { | |
346 | + intsrc = dsim_read(dsim, DSIM_INTSRC); | |
347 | + if (intsrc & INTSRC_RXDATDONE) { | |
348 | + dsim_write(dsim, INTSRC_RXDATDONE, DSIM_INTSRC); | |
349 | + return 0; | |
350 | + } | |
351 | + | |
352 | + udelay(1); | |
353 | + } while (--timeout); | |
354 | + | |
355 | + return -ETIMEDOUT; | |
356 | +} | |
357 | + | |
358 | +static int sec_mipi_dsim_wait_pll_stable(struct sec_mipi_dsim *dsim) | |
359 | +{ | |
360 | + uint32_t status; | |
361 | + ulong start; | |
362 | + | |
363 | + start = get_timer(0); /* Get current timestamp */ | |
364 | + | |
365 | + do { | |
366 | + status = dsim_read(dsim, DSIM_STATUS); | |
367 | + if (status & STATUS_PLLSTABLE) | |
368 | + return 0; | |
369 | + } while (get_timer(0) < (start + 100)); /* Wait 100ms */ | |
370 | + | |
371 | + return -ETIMEDOUT; | |
372 | +} | |
373 | + | |
374 | +static int sec_mipi_dsim_config_pll(struct sec_mipi_dsim *dsim) | |
375 | +{ | |
376 | + int ret; | |
377 | + uint32_t pllctrl = 0, status, data_lanes_en, stop; | |
378 | + | |
379 | + dsim_write(dsim, 0x8000, DSIM_PLLTMR); | |
380 | + | |
381 | + /* TODO: config dp/dn swap if requires */ | |
382 | + | |
383 | + pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN; | |
384 | + dsim_write(dsim, pllctrl, DSIM_PLLCTRL); | |
385 | + | |
386 | + ret = sec_mipi_dsim_wait_pll_stable(dsim); | |
387 | + if (ret) { | |
388 | + printf("wait for pll stable time out\n"); | |
389 | + return ret; | |
390 | + } | |
391 | + | |
392 | + /* wait for clk & data lanes to go to stop state */ | |
393 | + mdelay(1); | |
394 | + | |
395 | + data_lanes_en = (0x1 << dsim->lanes) - 1; | |
396 | + status = dsim_read(dsim, DSIM_STATUS); | |
397 | + if (!(status & STATUS_STOPSTATECLK)) { | |
398 | + printf("clock is not in stop state\n"); | |
399 | + return -EBUSY; | |
400 | + } | |
401 | + | |
402 | + stop = STATUS_GET_STOPSTATEDAT(status); | |
403 | + if ((stop & data_lanes_en) != data_lanes_en) { | |
404 | + printf("one or more data lanes is not in stop state\n"); | |
405 | + return -EBUSY; | |
406 | + } | |
407 | + | |
408 | + return 0; | |
409 | +} | |
410 | + | |
411 | +static void sec_mipi_dsim_set_main_mode(struct sec_mipi_dsim *dsim) | |
412 | +{ | |
413 | + uint32_t bpp, hfp_wc, hbp_wc, hsa_wc, wc; | |
414 | + uint32_t mdresol = 0, mvporch = 0, mhporch = 0, msync = 0; | |
415 | + struct display_timing *timings = &dsim->timings; | |
416 | + | |
417 | + mdresol |= MDRESOL_SET_MAINVRESOL(timings->vactive.typ) | | |
418 | + MDRESOL_SET_MAINHRESOL(timings->hactive.typ); | |
419 | + dsim_write(dsim, mdresol, DSIM_MDRESOL); | |
420 | + | |
421 | + mvporch |= MVPORCH_SET_MAINVBP(timings->vback_porch.typ) | | |
422 | + MVPORCH_SET_STABLEVFP(timings->vfront_porch.typ) | | |
423 | + MVPORCH_SET_CMDALLOW(0x0); | |
424 | + dsim_write(dsim, mvporch, DSIM_MVPORCH); | |
425 | + | |
426 | + bpp = mipi_dsi_pixel_format_to_bpp(dsim->format); | |
427 | + | |
428 | + | |
429 | + wc = DIV_ROUND_UP(timings->hfront_porch.typ* (bpp >> 3), | |
430 | + dsim->lanes); | |
431 | + hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ? | |
432 | + wc - MIPI_HFP_PKT_OVERHEAD : timings->hfront_porch.typ; | |
433 | + wc = DIV_ROUND_UP(timings->hback_porch.typ * (bpp >> 3), | |
434 | + dsim->lanes); | |
435 | + hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ? | |
436 | + wc - MIPI_HBP_PKT_OVERHEAD : timings->hback_porch.typ; | |
437 | + | |
438 | + mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) | | |
439 | + MHPORCH_SET_MAINHBP(hbp_wc); | |
440 | + | |
441 | + dsim_write(dsim, mhporch, DSIM_MHPORCH); | |
442 | + | |
443 | + wc = DIV_ROUND_UP(timings->hsync_len.typ * (bpp >> 3), | |
444 | + dsim->lanes); | |
445 | + hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ? | |
446 | + wc - MIPI_HSA_PKT_OVERHEAD : timings->hsync_len.typ; | |
447 | + | |
448 | + msync |= MSYNC_SET_MAINVSA(timings->vsync_len.typ) | | |
449 | + MSYNC_SET_MAINHSA(hsa_wc); | |
450 | + | |
451 | + debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc); | |
452 | + | |
453 | + dsim_write(dsim, msync, DSIM_MSYNC); | |
454 | +} | |
455 | + | |
456 | +static void sec_mipi_dsim_config_dpi(struct sec_mipi_dsim *dsim) | |
457 | +{ | |
458 | + uint32_t config = 0, rgb_status = 0, data_lanes_en; | |
459 | + | |
460 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) | |
461 | + rgb_status &= ~RGB_STATUS_CMDMODE_INSEL; | |
462 | + else | |
463 | + rgb_status |= RGB_STATUS_CMDMODE_INSEL; | |
464 | + | |
465 | + dsim_write(dsim, rgb_status, DSIM_RGB_STATUS); | |
466 | + | |
467 | + if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) | |
468 | + config |= CONFIG_CLKLANE_STOP_START; | |
469 | + | |
470 | + if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH) | |
471 | + config |= CONFIG_MFLUSH_VS; | |
472 | + | |
473 | + /* disable EoT packets in HS mode */ | |
474 | + if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET) | |
475 | + config |= CONFIG_EOT_R03; | |
476 | + | |
477 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
478 | + config |= CONFIG_VIDEOMODE; | |
479 | + | |
480 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) | |
481 | + config |= CONFIG_BURSTMODE; | |
482 | + | |
483 | + else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) | |
484 | + config |= CONFIG_SYNCINFORM; | |
485 | + | |
486 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) | |
487 | + config |= CONFIG_AUTOMODE; | |
488 | + | |
489 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) | |
490 | + config |= CONFIG_HSEDISABLEMODE; | |
491 | + | |
492 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP) | |
493 | + config |= CONFIG_HFPDISABLEMODE; | |
494 | + | |
495 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP) | |
496 | + config |= CONFIG_HBPDISABLEMODE; | |
497 | + | |
498 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA) | |
499 | + config |= CONFIG_HSADISABLEMODE; | |
500 | + } | |
501 | + | |
502 | + config |= CONFIG_SET_MAINVC(dsim->channel); | |
503 | + | |
504 | + if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) { | |
505 | + switch (dsim->format) { | |
506 | + case MIPI_DSI_FMT_RGB565: | |
507 | + config |= CONFIG_SET_MAINPIXFORMAT(0x4); | |
508 | + break; | |
509 | + case MIPI_DSI_FMT_RGB666_PACKED: | |
510 | + config |= CONFIG_SET_MAINPIXFORMAT(0x5); | |
511 | + break; | |
512 | + case MIPI_DSI_FMT_RGB666: | |
513 | + config |= CONFIG_SET_MAINPIXFORMAT(0x6); | |
514 | + break; | |
515 | + case MIPI_DSI_FMT_RGB888: | |
516 | + config |= CONFIG_SET_MAINPIXFORMAT(0x7); | |
517 | + break; | |
518 | + default: | |
519 | + config |= CONFIG_SET_MAINPIXFORMAT(0x7); | |
520 | + break; | |
521 | + } | |
522 | + } | |
523 | + | |
524 | + /* config data lanes number and enable lanes */ | |
525 | + data_lanes_en = (0x1 << dsim->lanes) - 1; | |
526 | + config |= CONFIG_SET_NUMOFDATLANE(dsim->lanes - 1); | |
527 | + config |= CONFIG_SET_LANEEN(0x1 | data_lanes_en << 1); | |
528 | + | |
529 | + debug("DSIM config 0x%x\n", config); | |
530 | + | |
531 | + dsim_write(dsim, config, DSIM_CONFIG); | |
532 | +} | |
533 | + | |
534 | +static void sec_mipi_dsim_config_cmd_lpm(struct sec_mipi_dsim *dsim, | |
535 | + bool enable) | |
536 | +{ | |
537 | + uint32_t escmode; | |
538 | + | |
539 | + escmode = dsim_read(dsim, DSIM_ESCMODE); | |
540 | + | |
541 | + if (enable) | |
542 | + escmode |= ESCMODE_CMDLPDT; | |
543 | + else | |
544 | + escmode &= ~ESCMODE_CMDLPDT; | |
545 | + | |
546 | + dsim_write(dsim, escmode, DSIM_ESCMODE); | |
547 | +} | |
548 | + | |
549 | +static void sec_mipi_dsim_config_dphy(struct sec_mipi_dsim *dsim) | |
550 | +{ | |
551 | + uint32_t phytiming = 0, phytiming1 = 0, phytiming2 = 0, timeout = 0; | |
552 | + | |
553 | + /* TODO: add a PHY timing table arranged by the pll Fout */ | |
554 | + | |
555 | + phytiming |= PHYTIMING_SET_M_TLPXCTL(6) | | |
556 | + PHYTIMING_SET_M_THSEXITCTL(11); | |
557 | + dsim_write(dsim, phytiming, DSIM_PHYTIMING); | |
558 | + | |
559 | + phytiming1 |= PHYTIMING1_SET_M_TCLKPRPRCTL(7) | | |
560 | + PHYTIMING1_SET_M_TCLKZEROCTL(38) | | |
561 | + PHYTIMING1_SET_M_TCLKPOSTCTL(13) | | |
562 | + PHYTIMING1_SET_M_TCLKTRAILCTL(8); | |
563 | + dsim_write(dsim, phytiming1, DSIM_PHYTIMING1); | |
564 | + | |
565 | + phytiming2 |= PHYTIMING2_SET_M_THSPRPRCTL(8) | | |
566 | + PHYTIMING2_SET_M_THSZEROCTL(13) | | |
567 | + PHYTIMING2_SET_M_THSTRAILCTL(11); | |
568 | + dsim_write(dsim, phytiming2, DSIM_PHYTIMING2); | |
569 | + | |
570 | + timeout |= TIMEOUT_SET_BTAOUT(0xf) | | |
571 | + TIMEOUT_SET_LPDRTOUT(0xf); | |
572 | + dsim_write(dsim, 0xf000f, DSIM_TIMEOUT); | |
573 | +} | |
574 | + | |
575 | +static void sec_mipi_dsim_write_pl_to_sfr_fifo(struct sec_mipi_dsim *dsim, | |
576 | + const void *payload, | |
577 | + size_t length) | |
578 | +{ | |
579 | + uint32_t pl_data; | |
580 | + | |
581 | + if (!length) | |
582 | + return; | |
583 | + | |
584 | + while (length >= 4) { | |
585 | + pl_data = get_unaligned_le32(payload); | |
586 | + dsim_write(dsim, pl_data, DSIM_PAYLOAD); | |
587 | + payload += 4; | |
588 | + length -= 4; | |
589 | + } | |
590 | + | |
591 | + pl_data = 0; | |
592 | + switch (length) { | |
593 | + case 3: | |
594 | + pl_data |= ((u8 *)payload)[2] << 16; | |
595 | + case 2: | |
596 | + pl_data |= ((u8 *)payload)[1] << 8; | |
597 | + case 1: | |
598 | + pl_data |= ((u8 *)payload)[0]; | |
599 | + dsim_write(dsim, pl_data, DSIM_PAYLOAD); | |
600 | + break; | |
601 | + } | |
602 | +} | |
603 | + | |
604 | +static void sec_mipi_dsim_write_ph_to_sfr_fifo(struct sec_mipi_dsim *dsim, | |
605 | + void *header, | |
606 | + bool use_lpm) | |
607 | +{ | |
608 | + uint32_t pkthdr; | |
609 | + | |
610 | + pkthdr = PKTHDR_SET_DATA1(((u8 *)header)[2]) | /* WC MSB */ | |
611 | + PKTHDR_SET_DATA0(((u8 *)header)[1]) | /* WC LSB */ | |
612 | + PKTHDR_SET_DI(((u8 *)header)[0]); /* Data ID */ | |
613 | + | |
614 | + dsim_write(dsim, pkthdr, DSIM_PKTHDR); | |
615 | +} | |
616 | + | |
617 | +static int sec_mipi_dsim_read_pl_from_sfr_fifo(struct sec_mipi_dsim *dsim, | |
618 | + void *payload, | |
619 | + size_t length) | |
620 | +{ | |
621 | + uint8_t data_type; | |
622 | + uint16_t word_count = 0; | |
623 | + uint32_t fifoctrl, ph, pl; | |
624 | + | |
625 | + fifoctrl = dsim_read(dsim, DSIM_FIFOCTRL); | |
626 | + | |
627 | + if (WARN_ON(fifoctrl & FIFOCTRL_EMPTYRX)) | |
628 | + return -EINVAL; | |
629 | + | |
630 | + ph = dsim_read(dsim, DSIM_RXFIFO); | |
631 | + data_type = PKTHDR_GET_DT(ph); | |
632 | + switch (data_type) { | |
633 | + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: | |
634 | + dev_err(dsim->dev, "peripheral report error: (0-7)%x, (8-15)%x\n", | |
635 | + PKTHDR_GET_DATA0(ph), PKTHDR_GET_DATA1(ph)); | |
636 | + return -EPROTO; | |
637 | + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: | |
638 | + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: | |
639 | + if (!WARN_ON(length < 2)) { | |
640 | + ((u8 *)payload)[1] = PKTHDR_GET_DATA1(ph); | |
641 | + word_count++; | |
642 | + } | |
643 | + /* fall through */ | |
644 | + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: | |
645 | + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: | |
646 | + ((u8 *)payload)[0] = PKTHDR_GET_DATA0(ph); | |
647 | + word_count++; | |
648 | + length = word_count; | |
649 | + break; | |
650 | + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: | |
651 | + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: | |
652 | + word_count = PKTHDR_GET_WC(ph); | |
653 | + if (word_count > length) { | |
654 | + dev_err(dsim->dev, "invalid receive buffer length\n"); | |
655 | + return -EINVAL; | |
656 | + } | |
657 | + | |
658 | + length = word_count; | |
659 | + | |
660 | + while (word_count >= 4) { | |
661 | + pl = dsim_read(dsim, DSIM_RXFIFO); | |
662 | + ((u8 *)payload)[0] = pl & 0xff; | |
663 | + ((u8 *)payload)[1] = (pl >> 8) & 0xff; | |
664 | + ((u8 *)payload)[2] = (pl >> 16) & 0xff; | |
665 | + ((u8 *)payload)[3] = (pl >> 24) & 0xff; | |
666 | + payload += 4; | |
667 | + word_count -= 4; | |
668 | + } | |
669 | + | |
670 | + if (word_count > 0) { | |
671 | + pl = dsim_read(dsim, DSIM_RXFIFO); | |
672 | + | |
673 | + switch (word_count) { | |
674 | + case 3: | |
675 | + ((u8 *)payload)[2] = (pl >> 16) & 0xff; | |
676 | + case 2: | |
677 | + ((u8 *)payload)[1] = (pl >> 8) & 0xff; | |
678 | + case 1: | |
679 | + ((u8 *)payload)[0] = pl & 0xff; | |
680 | + break; | |
681 | + } | |
682 | + } | |
683 | + | |
684 | + break; | |
685 | + default: | |
686 | + return -EINVAL; | |
687 | + } | |
688 | + | |
689 | + return length; | |
690 | +} | |
691 | + | |
692 | +static void sec_mipi_dsim_init_fifo_pointers(struct sec_mipi_dsim *dsim) | |
693 | +{ | |
694 | + uint32_t fifoctrl, fifo_ptrs; | |
695 | + | |
696 | + fifoctrl = dsim_read(dsim, DSIM_FIFOCTRL); | |
697 | + | |
698 | + fifo_ptrs = FIFOCTRL_NINITRX | | |
699 | + FIFOCTRL_NINITSFR | | |
700 | + FIFOCTRL_NINITI80 | | |
701 | + FIFOCTRL_NINITSUB | | |
702 | + FIFOCTRL_NINITMAIN; | |
703 | + | |
704 | + fifoctrl &= ~fifo_ptrs; | |
705 | + dsim_write(dsim, fifoctrl, DSIM_FIFOCTRL); | |
706 | + udelay(500); | |
707 | + | |
708 | + fifoctrl |= fifo_ptrs; | |
709 | + dsim_write(dsim, fifoctrl, DSIM_FIFOCTRL); | |
710 | + udelay(500); | |
711 | +} | |
712 | + | |
713 | + | |
714 | +static void sec_mipi_dsim_config_clkctrl(struct sec_mipi_dsim *dsim) | |
715 | +{ | |
716 | + uint32_t clkctrl = 0, data_lanes_en; | |
717 | + uint64_t byte_clk, esc_prescaler; | |
718 | + | |
719 | + clkctrl |= CLKCTRL_TXREQUESTHSCLK; | |
720 | + | |
721 | + /* using 1.5Gbps PHY */ | |
722 | + clkctrl |= CLKCTRL_DPHY_SEL_1P5G; | |
723 | + | |
724 | + clkctrl |= CLKCTRL_ESCCLKEN; | |
725 | + | |
726 | + clkctrl &= ~CLKCTRL_PLLBYPASS; | |
727 | + | |
728 | + clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL; | |
729 | + | |
730 | + clkctrl |= CLKCTRL_BYTECLKEN; | |
731 | + | |
732 | + data_lanes_en = (0x1 << dsim->lanes) - 1; | |
733 | + clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1); | |
734 | + | |
735 | + /* calculate esc prescaler from byte clock: | |
736 | + * EscClk = ByteClk / EscPrescaler; | |
737 | + */ | |
738 | + byte_clk = dsim->bit_clk >> 3; | |
739 | + esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ); | |
740 | + | |
741 | + clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler); | |
742 | + | |
743 | + debug("DSIM clkctrl 0x%x\n", clkctrl); | |
744 | + | |
745 | + dsim_write(dsim, clkctrl, DSIM_CLKCTRL); | |
746 | +} | |
747 | + | |
748 | +static void sec_mipi_dsim_set_standby(struct sec_mipi_dsim *dsim, | |
749 | + bool standby) | |
750 | +{ | |
751 | + uint32_t mdresol = 0; | |
752 | + | |
753 | + mdresol = dsim_read(dsim, DSIM_MDRESOL); | |
754 | + | |
755 | + if (standby) | |
756 | + mdresol |= MDRESOL_MAINSTANDBY; | |
757 | + else | |
758 | + mdresol &= ~MDRESOL_MAINSTANDBY; | |
759 | + | |
760 | + dsim_write(dsim, mdresol, DSIM_MDRESOL); | |
761 | +} | |
762 | + | |
763 | +static void sec_mipi_dsim_disable_clkctrl(struct sec_mipi_dsim *dsim) | |
764 | +{ | |
765 | + uint32_t clkctrl; | |
766 | + | |
767 | + clkctrl = dsim_read(dsim, DSIM_CLKCTRL); | |
768 | + | |
769 | + clkctrl &= ~CLKCTRL_TXREQUESTHSCLK; | |
770 | + | |
771 | + clkctrl &= ~CLKCTRL_ESCCLKEN; | |
772 | + | |
773 | + clkctrl &= ~CLKCTRL_BYTECLKEN; | |
774 | + | |
775 | + dsim_write(dsim, clkctrl, DSIM_CLKCTRL); | |
776 | +} | |
777 | + | |
778 | +static void sec_mipi_dsim_disable_pll(struct sec_mipi_dsim *dsim) | |
779 | +{ | |
780 | + uint32_t pllctrl; | |
781 | + | |
782 | + pllctrl = dsim_read(dsim, DSIM_PLLCTRL); | |
783 | + | |
784 | + pllctrl &= ~PLLCTRL_PLLEN; | |
785 | + | |
786 | + dsim_write(dsim, pllctrl, DSIM_PLLCTRL); | |
787 | +} | |
788 | + | |
789 | +static inline struct sec_mipi_dsim *host_to_dsi(struct mipi_dsi_host *host) | |
790 | +{ | |
791 | + return container_of(host, struct sec_mipi_dsim, dsi_host); | |
792 | +} | |
793 | + | |
794 | +static int sec_mipi_dsim_bridge_clk_set(struct sec_mipi_dsim *dsim_host) | |
795 | +{ | |
796 | + int bpp; | |
797 | + uint64_t pix_clk, bit_clk; | |
798 | + | |
799 | + bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format); | |
800 | + if (bpp < 0) | |
801 | + return -EINVAL; | |
802 | + | |
803 | + pix_clk = dsim_host->timings.pixelclock.typ; | |
804 | + bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes); | |
805 | + | |
806 | +#if 0 | |
807 | + if (bit_clk > dsim_host->max_data_rate) { | |
808 | + printf("request bit clk freq exceeds lane's maximum value\n"); | |
809 | + return -EINVAL; | |
810 | + } | |
811 | +#endif | |
812 | + | |
813 | + dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000); | |
814 | + dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000); | |
815 | + | |
816 | + if (dsim_host->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { | |
817 | + /* TODO: add PMS calculate and check | |
818 | + * Only support '1080p@60Hz' for now, | |
819 | + * add other modes support later | |
820 | + */ | |
821 | + dsim_host->pms = 0x4210; | |
822 | + } | |
823 | + | |
824 | + debug("%s: bitclk %llu pixclk %llu\n", __func__, dsim_host->bit_clk, dsim_host->pix_clk); | |
825 | + | |
826 | + return 0; | |
827 | +} | |
828 | + | |
829 | +static int sec_mipi_dsim_bridge_prepare(struct sec_mipi_dsim *dsim_host) | |
830 | +{ | |
831 | + int ret; | |
832 | + | |
833 | + /* At this moment, the dsim bridge's preceding encoder has | |
834 | + * already been enabled. So the dsim can be configed here | |
835 | + */ | |
836 | + | |
837 | + /* config main display mode */ | |
838 | + sec_mipi_dsim_set_main_mode(dsim_host); | |
839 | + | |
840 | + /* config dsim dpi */ | |
841 | + sec_mipi_dsim_config_dpi(dsim_host); | |
842 | + | |
843 | + /* config dsim pll */ | |
844 | + ret = sec_mipi_dsim_config_pll(dsim_host); | |
845 | + if (ret) { | |
846 | + printf("dsim pll config failed: %d\n", ret); | |
847 | + return ret; | |
848 | + } | |
849 | + | |
850 | + /* config dphy timings */ | |
851 | + sec_mipi_dsim_config_dphy(dsim_host); | |
852 | + | |
853 | + sec_mipi_dsim_init_fifo_pointers(dsim_host); | |
854 | + | |
855 | + /* config esc clock, byte clock and etc */ | |
856 | + sec_mipi_dsim_config_clkctrl(dsim_host); | |
857 | + | |
858 | + /* enable data transfer of dsim */ | |
859 | + sec_mipi_dsim_set_standby(dsim_host, true); | |
860 | + | |
861 | + return 0; | |
862 | +} | |
863 | + | |
864 | +static int sec_mipi_dsim_host_attach(struct mipi_dsi_host *host, | |
865 | + struct mipi_dsi_device *device) | |
866 | +{ | |
867 | + struct sec_mipi_dsim *dsi = host_to_dsi(host); | |
868 | + | |
869 | + if (!device->lanes || device->lanes > dsi->max_data_lanes) { | |
870 | + printf("invalid data lanes number\n"); | |
871 | + return -EINVAL; | |
872 | + } | |
873 | + | |
874 | + if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO) || | |
875 | + !((device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) || | |
876 | + (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) { | |
877 | + printf("unsupported dsi mode\n"); | |
878 | + return -EINVAL; | |
879 | + } | |
880 | + | |
881 | + if (device->format != MIPI_DSI_FMT_RGB888 && | |
882 | + device->format != MIPI_DSI_FMT_RGB565 && | |
883 | + device->format != MIPI_DSI_FMT_RGB666 && | |
884 | + device->format != MIPI_DSI_FMT_RGB666_PACKED) { | |
885 | + printf("unsupported pixel format: %#x\n", device->format); | |
886 | + return -EINVAL; | |
887 | + } | |
888 | + | |
889 | + dsi->lanes = device->lanes; | |
890 | + dsi->channel = device->channel; | |
891 | + dsi->format = device->format; | |
892 | + dsi->mode_flags = device->mode_flags; | |
893 | + | |
894 | + debug("lanes %u, channel %u, format 0x%x, mode_flags 0x%lx\n", dsi->lanes, | |
895 | + dsi->channel, dsi->format, dsi->mode_flags); | |
896 | + | |
897 | + sec_mipi_dsim_bridge_clk_set(dsi); | |
898 | + sec_mipi_dsim_bridge_prepare(dsi); | |
899 | + | |
900 | + return 0; | |
901 | +} | |
902 | + | |
903 | +static ssize_t sec_mipi_dsi_host_transfer(struct mipi_dsi_host *host, | |
904 | + const struct mipi_dsi_msg *msg) | |
905 | +{ | |
906 | + struct sec_mipi_dsim *dsim = host_to_dsi(host); | |
907 | + int ret, nb_bytes; | |
908 | + bool use_lpm; | |
909 | + struct mipi_dsi_packet packet; | |
910 | + | |
911 | +#ifdef DEBUG | |
912 | + int i = 0; | |
913 | + u8 *p = msg->tx_buf; | |
914 | + | |
915 | + printf("sec_mipi_dsi_host_transfer\n"); | |
916 | + for (i; i < msg->tx_len; i++) { | |
917 | + printf("0x%.2x ", *(u8 *)p); | |
918 | + p++; | |
919 | + } | |
920 | + printf("\n"); | |
921 | +#endif | |
922 | + | |
923 | + ret = mipi_dsi_create_packet(&packet, msg); | |
924 | + if (ret) { | |
925 | + dev_err(dsim->dev, "failed to create dsi packet: %d\n", ret); | |
926 | + return ret; | |
927 | + } | |
928 | + | |
929 | + /* config LPM for CMD TX */ | |
930 | + use_lpm = msg->flags & MIPI_DSI_MSG_USE_LPM ? true : false; | |
931 | + sec_mipi_dsim_config_cmd_lpm(dsim, use_lpm); | |
932 | + | |
933 | + if (packet.payload_length) { /* Long Packet case */ | |
934 | + /* write packet payload */ | |
935 | + sec_mipi_dsim_write_pl_to_sfr_fifo(dsim, | |
936 | + packet.payload, | |
937 | + packet.payload_length); | |
938 | + | |
939 | + /* write packet header */ | |
940 | + sec_mipi_dsim_write_ph_to_sfr_fifo(dsim, | |
941 | + packet.header, | |
942 | + use_lpm); | |
943 | + | |
944 | + ret = sec_mipi_dsim_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT); | |
945 | + if (ret) { | |
946 | + dev_err(dsim->dev, "wait tx done timeout!\n"); | |
947 | + return -EBUSY; | |
948 | + } | |
949 | + } else { | |
950 | + /* write packet header */ | |
951 | + sec_mipi_dsim_write_ph_to_sfr_fifo(dsim, | |
952 | + packet.header, | |
953 | + use_lpm); | |
954 | + | |
955 | + ret = sec_mipi_dsim_wait_for_hdr_done(dsim, MIPI_FIFO_TIMEOUT); | |
956 | + if (ret) { | |
957 | + dev_err(dsim->dev, "wait pkthdr tx done time out\n"); | |
958 | + return -EBUSY; | |
959 | + } | |
960 | + } | |
961 | + | |
962 | + /* read packet payload */ | |
963 | + if (unlikely(msg->rx_buf)) { | |
964 | + ret = sec_mipi_dsim_wait_for_rx_done(dsim, | |
965 | + MIPI_FIFO_TIMEOUT); | |
966 | + if (ret) { | |
967 | + dev_err(dsim->dev, "wait rx done time out\n"); | |
968 | + return -EBUSY; | |
969 | + } | |
970 | + | |
971 | + ret = sec_mipi_dsim_read_pl_from_sfr_fifo(dsim, | |
972 | + msg->rx_buf, | |
973 | + msg->rx_len); | |
974 | + if (ret < 0) | |
975 | + return ret; | |
976 | + nb_bytes = msg->rx_len; | |
977 | + } else { | |
978 | + nb_bytes = packet.size; | |
979 | + } | |
980 | + | |
981 | + return nb_bytes; | |
982 | + | |
983 | +} | |
984 | + | |
985 | + | |
986 | +static const struct mipi_dsi_host_ops sec_mipi_dsim_host_ops = { | |
987 | + .attach = sec_mipi_dsim_host_attach, | |
988 | + .transfer = sec_mipi_dsi_host_transfer, | |
989 | +}; | |
990 | + | |
991 | +static int sec_mipi_dsim_init(struct udevice *dev, | |
992 | + struct mipi_dsi_device *device, | |
993 | + struct display_timing *timings, | |
994 | + unsigned int max_data_lanes, | |
995 | + const struct mipi_dsi_phy_ops *phy_ops) | |
996 | +{ | |
997 | + struct sec_mipi_dsim *dsi = dev_get_priv(dev); | |
998 | + | |
999 | + dsi->max_data_lanes = max_data_lanes; | |
1000 | + dsi->device = device; | |
1001 | + dsi->dsi_host.ops = &sec_mipi_dsim_host_ops; | |
1002 | + device->host = &dsi->dsi_host; | |
1003 | + | |
1004 | + dsi->base = (void *)dev_read_addr(device->dev); | |
1005 | + if ((fdt_addr_t)dsi->base == FDT_ADDR_T_NONE) { | |
1006 | + dev_err(device->dev, "dsi dt register address error\n"); | |
1007 | + return -EINVAL; | |
1008 | + } | |
1009 | + | |
1010 | + dsi->timings = *timings; | |
1011 | + | |
1012 | + return 0; | |
1013 | +} | |
1014 | + | |
1015 | +static int sec_mipi_dsim_enable(struct udevice *dev) | |
1016 | +{ | |
1017 | + return 0; | |
1018 | +} | |
1019 | + | |
1020 | +static int sec_mipi_dsim_disable(struct udevice *dev) | |
1021 | +{ | |
1022 | + uint32_t intsrc; | |
1023 | + struct sec_mipi_dsim *dsim_host = dev_get_priv(dev); | |
1024 | + | |
1025 | + /* disable data transfer of dsim */ | |
1026 | + sec_mipi_dsim_set_standby(dsim_host, false); | |
1027 | + | |
1028 | + /* disable esc clock & byte clock */ | |
1029 | + sec_mipi_dsim_disable_clkctrl(dsim_host); | |
1030 | + | |
1031 | + /* disable dsim pll */ | |
1032 | + sec_mipi_dsim_disable_pll(dsim_host); | |
1033 | + | |
1034 | + /* Clear all intsrc */ | |
1035 | + intsrc = dsim_read(dsim_host, DSIM_INTSRC); | |
1036 | + dsim_write(dsim_host, intsrc, DSIM_INTSRC); | |
1037 | + | |
1038 | + return 0; | |
1039 | +} | |
1040 | + | |
1041 | +struct dsi_host_ops sec_mipi_dsim_ops = { | |
1042 | + .init = sec_mipi_dsim_init, | |
1043 | + .enable = sec_mipi_dsim_enable, | |
1044 | + .disable = sec_mipi_dsim_disable, | |
1045 | +}; | |
1046 | + | |
1047 | +static int sec_mipi_dsim_probe(struct udevice *dev) | |
1048 | +{ | |
1049 | + return 0; | |
1050 | +} | |
1051 | + | |
1052 | +static const struct udevice_id sec_mipi_dsim_ids[] = { | |
1053 | + { .compatible = "samsung,sec-mipi-dsi" }, | |
1054 | + { } | |
1055 | +}; | |
1056 | + | |
1057 | +U_BOOT_DRIVER(sec_mipi_dsim) = { | |
1058 | + .name = "sec_mipi_dsim", | |
1059 | + .id = UCLASS_DSI_HOST, | |
1060 | + .of_match = sec_mipi_dsim_ids, | |
1061 | + .probe = sec_mipi_dsim_probe, | |
1062 | + .remove = sec_mipi_dsim_disable, | |
1063 | + .ops = &sec_mipi_dsim_ops, | |
1064 | + .priv_auto_alloc_size = sizeof(struct sec_mipi_dsim), | |
1065 | +}; |
drivers/video/mxsfb.c
... | ... | @@ -20,12 +20,17 @@ |
20 | 20 | #include <asm/arch/sys_proto.h> |
21 | 21 | #include <asm/mach-imx/dma.h> |
22 | 22 | #include <asm/io.h> |
23 | +#include <reset.h> | |
24 | +#include <panel.h> | |
25 | +#include <video_bridge.h> | |
26 | +#include <video_link.h> | |
23 | 27 | |
24 | 28 | #include "videomodes.h" |
25 | 29 | #include <linux/string.h> |
26 | 30 | #include <linux/list.h> |
27 | 31 | #include <linux/fb.h> |
28 | 32 | #include <mxsfb.h> |
33 | +#include <dm/device-internal.h> | |
29 | 34 | |
30 | 35 | #ifdef CONFIG_VIDEO_GIS |
31 | 36 | #include <gis.h> |
... | ... | @@ -62,7 +67,7 @@ |
62 | 67 | * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0 |
63 | 68 | */ |
64 | 69 | |
65 | -static void mxs_lcd_init(phys_addr_t reg_base, u32 fb_addr, struct ctfb_res_modes *mode, int bpp) | |
70 | +static void mxs_lcd_init(phys_addr_t reg_base, u32 fb_addr, struct ctfb_res_modes *mode, int bpp, bool bridge, bool enable_pol) | |
66 | 71 | { |
67 | 72 | struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)(reg_base); |
68 | 73 | uint32_t word_len = 0, bus_width = 0; |
69 | 74 | |
... | ... | @@ -104,15 +109,25 @@ |
104 | 109 | writel(valid_data << LCDIF_CTRL1_BYTE_PACKING_FORMAT_OFFSET, |
105 | 110 | ®s->hw_lcdif_ctrl1); |
106 | 111 | |
112 | + if (bridge) | |
113 | + writel(LCDIF_CTRL2_OUTSTANDING_REQS_REQ_16, ®s->hw_lcdif_ctrl2); | |
114 | + | |
107 | 115 | mxsfb_system_setup(); |
108 | 116 | |
109 | 117 | writel((mode->yres << LCDIF_TRANSFER_COUNT_V_COUNT_OFFSET) | mode->xres, |
110 | 118 | ®s->hw_lcdif_transfer_count); |
111 | 119 | |
112 | - writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | | |
113 | - LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | | |
114 | - LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | | |
115 | - mode->vsync_len, ®s->hw_lcdif_vdctrl0); | |
120 | + if (!enable_pol) | |
121 | + writel(LCDIF_VDCTRL0_ENABLE_PRESENT | | |
122 | + LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | | |
123 | + LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | | |
124 | + mode->vsync_len, ®s->hw_lcdif_vdctrl0); | |
125 | + else | |
126 | + writel(LCDIF_VDCTRL0_ENABLE_PRESENT | LCDIF_VDCTRL0_ENABLE_POL | | |
127 | + LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT | | |
128 | + LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | | |
129 | + mode->vsync_len, ®s->hw_lcdif_vdctrl0); | |
130 | + | |
116 | 131 | writel(mode->upper_margin + mode->lower_margin + |
117 | 132 | mode->vsync_len + mode->yres, |
118 | 133 | ®s->hw_lcdif_vdctrl1); |
119 | 134 | |
... | ... | @@ -145,10 +160,10 @@ |
145 | 160 | writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); |
146 | 161 | } |
147 | 162 | |
148 | -static int mxs_probe_common(phys_addr_t reg_base, struct ctfb_res_modes *mode, int bpp, u32 fb) | |
163 | +static int mxs_probe_common(phys_addr_t reg_base, struct ctfb_res_modes *mode, int bpp, u32 fb, bool bridge, bool enable_pol) | |
149 | 164 | { |
150 | 165 | /* Start framebuffer */ |
151 | - mxs_lcd_init(reg_base, fb, mode, bpp); | |
166 | + mxs_lcd_init(reg_base, fb, mode, bpp, bridge, enable_pol); | |
152 | 167 | |
153 | 168 | #ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM |
154 | 169 | /* |
... | ... | @@ -324,7 +339,7 @@ |
324 | 339 | |
325 | 340 | printf("%s\n", panel.modeIdent); |
326 | 341 | |
327 | - ret = mxs_probe_common(panel.isaBase, &mode, bpp, (u32)fb); | |
342 | + ret = mxs_probe_common(panel.isaBase, &mode, bpp, (u32)fb, false, true); | |
328 | 343 | if (ret) |
329 | 344 | goto dealloc_fb; |
330 | 345 | |
331 | 346 | |
... | ... | @@ -344,8 +359,74 @@ |
344 | 359 | |
345 | 360 | struct mxsfb_priv { |
346 | 361 | fdt_addr_t reg_base; |
362 | + struct udevice *disp_dev; | |
363 | + | |
364 | +#if IS_ENABLED(CONFIG_DM_RESET) | |
365 | + struct reset_ctl_bulk soft_resetn; | |
366 | + struct reset_ctl_bulk clk_enable; | |
367 | +#endif | |
347 | 368 | }; |
348 | 369 | |
370 | +#if IS_ENABLED(CONFIG_DM_RESET) | |
371 | +static int lcdif_rstc_reset(struct reset_ctl_bulk *rstc, bool assert) | |
372 | +{ | |
373 | + int ret; | |
374 | + | |
375 | + if (!rstc) | |
376 | + return 0; | |
377 | + | |
378 | + ret = assert ? reset_assert_bulk(rstc) : | |
379 | + reset_deassert_bulk(rstc); | |
380 | + | |
381 | + return ret; | |
382 | +} | |
383 | + | |
384 | +static int lcdif_of_parse_resets(struct udevice *dev) | |
385 | +{ | |
386 | + int ret; | |
387 | + ofnode parent, child; | |
388 | + struct ofnode_phandle_args args; | |
389 | + struct reset_ctl_bulk rstc; | |
390 | + const char *compat; | |
391 | + uint32_t rstc_num = 0; | |
392 | + | |
393 | + struct mxsfb_priv *priv = dev_get_priv(dev); | |
394 | + | |
395 | + ret = dev_read_phandle_with_args(dev, "resets", "#reset-cells", 0, | |
396 | + 0, &args); | |
397 | + if (ret) | |
398 | + return ret; | |
399 | + | |
400 | + parent = args.node; | |
401 | + ofnode_for_each_subnode(child, parent) { | |
402 | + compat = ofnode_get_property(child, "compatible", NULL); | |
403 | + if (!compat) | |
404 | + continue; | |
405 | + | |
406 | + ret = reset_get_bulk_nodev(child, &rstc); | |
407 | + if (ret) | |
408 | + continue; | |
409 | + | |
410 | + if (!of_compat_cmp("lcdif,soft-resetn", compat, 0)) { | |
411 | + priv->soft_resetn = rstc; | |
412 | + rstc_num++; | |
413 | + } else if (!of_compat_cmp("lcdif,clk-enable", compat, 0)) { | |
414 | + priv->clk_enable = rstc; | |
415 | + rstc_num++; | |
416 | + } | |
417 | + else | |
418 | + dev_warn(dev, "invalid lcdif reset node: %s\n", compat); | |
419 | + } | |
420 | + | |
421 | + if (!rstc_num) { | |
422 | + dev_err(dev, "no invalid reset control exists\n"); | |
423 | + return -EINVAL; | |
424 | + } | |
425 | + | |
426 | + return 0; | |
427 | +} | |
428 | +#endif | |
429 | + | |
349 | 430 | static int mxs_of_get_timings(struct udevice *dev, |
350 | 431 | struct display_timing *timings, |
351 | 432 | u32 *bpp) |
... | ... | @@ -353,6 +434,7 @@ |
353 | 434 | int ret = 0; |
354 | 435 | u32 display_phandle; |
355 | 436 | ofnode display_node; |
437 | + struct mxsfb_priv *priv = dev_get_priv(dev); | |
356 | 438 | |
357 | 439 | ret = ofnode_read_u32(dev_ofnode(dev), "display", &display_phandle); |
358 | 440 | if (ret) { |
... | ... | @@ -373,10 +455,19 @@ |
373 | 455 | return -EINVAL; |
374 | 456 | } |
375 | 457 | |
376 | - ret = ofnode_decode_display_timing(display_node, 0, timings); | |
377 | - if (ret) { | |
378 | - dev_err(dev, "failed to get any display timings\n"); | |
379 | - return -EINVAL; | |
458 | + priv->disp_dev = video_link_get_next_device(dev); | |
459 | + if (priv->disp_dev) { | |
460 | + ret = video_link_get_display_timings(timings); | |
461 | + if (ret) { | |
462 | + dev_err(dev, "failed to get any video link display timings\n"); | |
463 | + return -EINVAL; | |
464 | + } | |
465 | + } else { | |
466 | + ret = ofnode_decode_display_timing(display_node, 0, timings); | |
467 | + if (ret) { | |
468 | + dev_err(dev, "failed to get any display timings\n"); | |
469 | + return -EINVAL; | |
470 | + } | |
380 | 471 | } |
381 | 472 | |
382 | 473 | return ret; |
383 | 474 | |
384 | 475 | |
... | ... | @@ -393,20 +484,72 @@ |
393 | 484 | u32 bpp = 0; |
394 | 485 | u32 fb_start, fb_end; |
395 | 486 | int ret; |
487 | + bool enable_pol = true, enable_bridge = false; | |
396 | 488 | |
397 | 489 | debug("%s() plat: base 0x%lx, size 0x%x\n", |
398 | 490 | __func__, plat->base, plat->size); |
399 | 491 | |
400 | - ret = mxs_of_get_timings(dev, &timings, &bpp); | |
401 | - if (ret) | |
402 | - return ret; | |
403 | - | |
404 | 492 | priv->reg_base = dev_read_addr(dev); |
405 | 493 | if (priv->reg_base == FDT_ADDR_T_NONE) { |
406 | 494 | dev_err(dev, "lcdif base address is not found\n"); |
407 | 495 | return -EINVAL; |
408 | 496 | } |
409 | 497 | |
498 | + ret = mxs_of_get_timings(dev, &timings, &bpp); | |
499 | + if (ret) | |
500 | + return ret; | |
501 | + | |
502 | +#if IS_ENABLED(CONFIG_DM_RESET) | |
503 | + ret = lcdif_of_parse_resets(dev); | |
504 | + if (!ret) { | |
505 | + ret = lcdif_rstc_reset(&priv->soft_resetn, false); | |
506 | + if (ret) { | |
507 | + dev_err(dev, "deassert soft_resetn failed\n"); | |
508 | + return ret; | |
509 | + } | |
510 | + | |
511 | + ret = lcdif_rstc_reset(&priv->clk_enable, true); | |
512 | + if (ret) { | |
513 | + dev_err(dev, "assert clk_enable failed\n"); | |
514 | + return ret; | |
515 | + } | |
516 | + } | |
517 | +#endif | |
518 | + | |
519 | + if (priv->disp_dev) { | |
520 | +#if IS_ENABLED(CONFIG_VIDEO_BRIDGE) | |
521 | + if (device_get_uclass_id(priv->disp_dev) == UCLASS_VIDEO_BRIDGE) { | |
522 | + ret = video_bridge_attach(priv->disp_dev); | |
523 | + if (ret) { | |
524 | + dev_err(dev, "fail to attach bridge\n"); | |
525 | + return ret; | |
526 | + } | |
527 | + | |
528 | + ret = video_bridge_set_backlight(priv->disp_dev, 80); | |
529 | + if (ret) { | |
530 | + dev_err(dev, "fail to set backlight\n"); | |
531 | + return ret; | |
532 | + } | |
533 | + | |
534 | + enable_bridge = true; | |
535 | + | |
536 | + /* sec dsim needs enable ploarity at low, default we set to high */ | |
537 | + if (dev_read_bool(dev, "enable_polarity_low")) | |
538 | + enable_pol = false; | |
539 | + | |
540 | + } | |
541 | +#endif | |
542 | + | |
543 | + if (device_get_uclass_id(priv->disp_dev) == UCLASS_PANEL) { | |
544 | + ret = panel_enable_backlight(priv->disp_dev); | |
545 | + if (ret) { | |
546 | + dev_err(dev, "panel %s enable backlight error %d\n", | |
547 | + priv->disp_dev->name, ret); | |
548 | + return ret; | |
549 | + } | |
550 | + } | |
551 | + } | |
552 | + | |
410 | 553 | mode.xres = timings.hactive.typ; |
411 | 554 | mode.yres = timings.vactive.typ; |
412 | 555 | mode.left_margin = timings.hback_porch.typ; |
... | ... | @@ -417,7 +560,7 @@ |
417 | 560 | mode.vsync_len = timings.vsync_len.typ; |
418 | 561 | mode.pixclock = HZ2PS(timings.pixelclock.typ); |
419 | 562 | |
420 | - ret = mxs_probe_common(priv->reg_base, &mode, bpp, plat->base); | |
563 | + ret = mxs_probe_common(priv->reg_base, &mode, bpp, plat->base, enable_bridge, enable_pol); | |
421 | 564 | if (ret) |
422 | 565 | return ret; |
423 | 566 | |
424 | 567 | |
425 | 568 | |
... | ... | @@ -456,34 +599,10 @@ |
456 | 599 | static int mxs_video_bind(struct udevice *dev) |
457 | 600 | { |
458 | 601 | struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); |
459 | - struct display_timing timings; | |
460 | - u32 bpp = 0; | |
461 | - u32 bytes_pp = 0; | |
462 | - int ret; | |
463 | 602 | |
464 | - ret = mxs_of_get_timings(dev, &timings, &bpp); | |
465 | - if (ret) | |
466 | - return ret; | |
603 | + /* Max size supported by LCDIF, because in bind, we can't probe panel */ | |
604 | + plat->size = 1920 * 1080 *4 * 2; | |
467 | 605 | |
468 | - switch (bpp) { | |
469 | - case 32: | |
470 | - case 24: | |
471 | - case 18: | |
472 | - bytes_pp = 4; | |
473 | - break; | |
474 | - case 16: | |
475 | - bytes_pp = 2; | |
476 | - break; | |
477 | - case 8: | |
478 | - bytes_pp = 1; | |
479 | - break; | |
480 | - default: | |
481 | - dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp); | |
482 | - return -EINVAL; | |
483 | - } | |
484 | - | |
485 | - plat->size = timings.hactive.typ * timings.vactive.typ * bytes_pp; | |
486 | - | |
487 | 606 | return 0; |
488 | 607 | } |
489 | 608 | |
... | ... | @@ -492,6 +611,11 @@ |
492 | 611 | struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); |
493 | 612 | struct mxsfb_priv *priv = dev_get_priv(dev); |
494 | 613 | |
614 | + debug("%s\n", __func__); | |
615 | + | |
616 | + if (priv->disp_dev) | |
617 | + device_remove(priv->disp_dev, DM_REMOVE_NORMAL); | |
618 | + | |
495 | 619 | mxs_remove_common(priv->reg_base, plat->base); |
496 | 620 | |
497 | 621 | return 0; |
... | ... | @@ -501,6 +625,8 @@ |
501 | 625 | { .compatible = "fsl,imx23-lcdif" }, |
502 | 626 | { .compatible = "fsl,imx28-lcdif" }, |
503 | 627 | { .compatible = "fsl,imx7ulp-lcdif" }, |
628 | + { .compatible = "fsl,imx8mm-lcdif" }, | |
629 | + { .compatible = "fsl,imx8mn-lcdif" }, | |
504 | 630 | { /* sentinel */ } |
505 | 631 | }; |
506 | 632 |
drivers/video/raydium-rm67191.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright 2019 NXP | |
4 | + * | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm.h> | |
9 | +#include <mipi_dsi.h> | |
10 | +#include <panel.h> | |
11 | +#include <asm/gpio.h> | |
12 | +#include <linux/err.h> | |
13 | + | |
14 | +#define CMD_TABLE_LEN 2 | |
15 | +typedef u8 cmd_set_table[CMD_TABLE_LEN]; | |
16 | + | |
17 | +/* Write Manufacture Command Set Control */ | |
18 | +#define WRMAUCCTR 0xFE | |
19 | + | |
20 | +struct rm67191_panel_priv { | |
21 | + struct gpio_desc reset; | |
22 | + unsigned int lanes; | |
23 | + enum mipi_dsi_pixel_format format; | |
24 | + unsigned long mode_flags; | |
25 | +}; | |
26 | + | |
27 | +/* Manufacturer Command Set pages (CMD2) */ | |
28 | +static const cmd_set_table manufacturer_cmd_set[] = { | |
29 | + {0xFE, 0x0B}, | |
30 | + {0x28, 0x40}, | |
31 | + {0x29, 0x4F}, | |
32 | + {0xFE, 0x0E}, | |
33 | + {0x4B, 0x00}, | |
34 | + {0x4C, 0x0F}, | |
35 | + {0x4D, 0x20}, | |
36 | + {0x4E, 0x40}, | |
37 | + {0x4F, 0x60}, | |
38 | + {0x50, 0xA0}, | |
39 | + {0x51, 0xC0}, | |
40 | + {0x52, 0xE0}, | |
41 | + {0x53, 0xFF}, | |
42 | + {0xFE, 0x0D}, | |
43 | + {0x18, 0x08}, | |
44 | + {0x42, 0x00}, | |
45 | + {0x08, 0x41}, | |
46 | + {0x46, 0x02}, | |
47 | + {0x72, 0x09}, | |
48 | + {0xFE, 0x0A}, | |
49 | + {0x24, 0x17}, | |
50 | + {0x04, 0x07}, | |
51 | + {0x1A, 0x0C}, | |
52 | + {0x0F, 0x44}, | |
53 | + {0xFE, 0x04}, | |
54 | + {0x00, 0x0C}, | |
55 | + {0x05, 0x08}, | |
56 | + {0x06, 0x08}, | |
57 | + {0x08, 0x08}, | |
58 | + {0x09, 0x08}, | |
59 | + {0x0A, 0xE6}, | |
60 | + {0x0B, 0x8C}, | |
61 | + {0x1A, 0x12}, | |
62 | + {0x1E, 0xE0}, | |
63 | + {0x29, 0x93}, | |
64 | + {0x2A, 0x93}, | |
65 | + {0x2F, 0x02}, | |
66 | + {0x31, 0x02}, | |
67 | + {0x33, 0x05}, | |
68 | + {0x37, 0x2D}, | |
69 | + {0x38, 0x2D}, | |
70 | + {0x3A, 0x1E}, | |
71 | + {0x3B, 0x1E}, | |
72 | + {0x3D, 0x27}, | |
73 | + {0x3F, 0x80}, | |
74 | + {0x40, 0x40}, | |
75 | + {0x41, 0xE0}, | |
76 | + {0x4F, 0x2F}, | |
77 | + {0x50, 0x1E}, | |
78 | + {0xFE, 0x06}, | |
79 | + {0x00, 0xCC}, | |
80 | + {0x05, 0x05}, | |
81 | + {0x07, 0xA2}, | |
82 | + {0x08, 0xCC}, | |
83 | + {0x0D, 0x03}, | |
84 | + {0x0F, 0xA2}, | |
85 | + {0x32, 0xCC}, | |
86 | + {0x37, 0x05}, | |
87 | + {0x39, 0x83}, | |
88 | + {0x3A, 0xCC}, | |
89 | + {0x41, 0x04}, | |
90 | + {0x43, 0x83}, | |
91 | + {0x44, 0xCC}, | |
92 | + {0x49, 0x05}, | |
93 | + {0x4B, 0xA2}, | |
94 | + {0x4C, 0xCC}, | |
95 | + {0x51, 0x03}, | |
96 | + {0x53, 0xA2}, | |
97 | + {0x75, 0xCC}, | |
98 | + {0x7A, 0x03}, | |
99 | + {0x7C, 0x83}, | |
100 | + {0x7D, 0xCC}, | |
101 | + {0x82, 0x02}, | |
102 | + {0x84, 0x83}, | |
103 | + {0x85, 0xEC}, | |
104 | + {0x86, 0x0F}, | |
105 | + {0x87, 0xFF}, | |
106 | + {0x88, 0x00}, | |
107 | + {0x8A, 0x02}, | |
108 | + {0x8C, 0xA2}, | |
109 | + {0x8D, 0xEA}, | |
110 | + {0x8E, 0x01}, | |
111 | + {0x8F, 0xE8}, | |
112 | + {0xFE, 0x06}, | |
113 | + {0x90, 0x0A}, | |
114 | + {0x92, 0x06}, | |
115 | + {0x93, 0xA0}, | |
116 | + {0x94, 0xA8}, | |
117 | + {0x95, 0xEC}, | |
118 | + {0x96, 0x0F}, | |
119 | + {0x97, 0xFF}, | |
120 | + {0x98, 0x00}, | |
121 | + {0x9A, 0x02}, | |
122 | + {0x9C, 0xA2}, | |
123 | + {0xAC, 0x04}, | |
124 | + {0xFE, 0x06}, | |
125 | + {0xB1, 0x12}, | |
126 | + {0xB2, 0x17}, | |
127 | + {0xB3, 0x17}, | |
128 | + {0xB4, 0x17}, | |
129 | + {0xB5, 0x17}, | |
130 | + {0xB6, 0x11}, | |
131 | + {0xB7, 0x08}, | |
132 | + {0xB8, 0x09}, | |
133 | + {0xB9, 0x06}, | |
134 | + {0xBA, 0x07}, | |
135 | + {0xBB, 0x17}, | |
136 | + {0xBC, 0x17}, | |
137 | + {0xBD, 0x17}, | |
138 | + {0xBE, 0x17}, | |
139 | + {0xBF, 0x17}, | |
140 | + {0xC0, 0x17}, | |
141 | + {0xC1, 0x17}, | |
142 | + {0xC2, 0x17}, | |
143 | + {0xC3, 0x17}, | |
144 | + {0xC4, 0x0F}, | |
145 | + {0xC5, 0x0E}, | |
146 | + {0xC6, 0x00}, | |
147 | + {0xC7, 0x01}, | |
148 | + {0xC8, 0x10}, | |
149 | + {0xFE, 0x06}, | |
150 | + {0x95, 0xEC}, | |
151 | + {0x8D, 0xEE}, | |
152 | + {0x44, 0xEC}, | |
153 | + {0x4C, 0xEC}, | |
154 | + {0x32, 0xEC}, | |
155 | + {0x3A, 0xEC}, | |
156 | + {0x7D, 0xEC}, | |
157 | + {0x75, 0xEC}, | |
158 | + {0x00, 0xEC}, | |
159 | + {0x08, 0xEC}, | |
160 | + {0x85, 0xEC}, | |
161 | + {0xA6, 0x21}, | |
162 | + {0xA7, 0x05}, | |
163 | + {0xA9, 0x06}, | |
164 | + {0x82, 0x06}, | |
165 | + {0x41, 0x06}, | |
166 | + {0x7A, 0x07}, | |
167 | + {0x37, 0x07}, | |
168 | + {0x05, 0x06}, | |
169 | + {0x49, 0x06}, | |
170 | + {0x0D, 0x04}, | |
171 | + {0x51, 0x04}, | |
172 | +}; | |
173 | + | |
174 | +static const struct display_timing default_timing = { | |
175 | + .pixelclock.typ = 132000000, | |
176 | + .hactive.typ = 1080, | |
177 | + .hfront_porch.typ = 20, | |
178 | + .hback_porch.typ = 34, | |
179 | + .hsync_len.typ = 2, | |
180 | + .vactive.typ = 1920, | |
181 | + .vfront_porch.typ = 10, | |
182 | + .vback_porch.typ = 4, | |
183 | + .vsync_len.typ = 2, | |
184 | + .flags = DISPLAY_FLAGS_HSYNC_LOW | | |
185 | + DISPLAY_FLAGS_VSYNC_LOW | | |
186 | + DISPLAY_FLAGS_DE_LOW | | |
187 | + DISPLAY_FLAGS_PIXDATA_NEGEDGE, | |
188 | +}; | |
189 | + | |
190 | + | |
191 | +static u8 color_format_from_dsi_format(enum mipi_dsi_pixel_format format) | |
192 | +{ | |
193 | + switch (format) { | |
194 | + case MIPI_DSI_FMT_RGB565: | |
195 | + return 0x55; | |
196 | + case MIPI_DSI_FMT_RGB666: | |
197 | + case MIPI_DSI_FMT_RGB666_PACKED: | |
198 | + return 0x66; | |
199 | + case MIPI_DSI_FMT_RGB888: | |
200 | + return 0x77; | |
201 | + default: | |
202 | + return 0x77; /* for backward compatibility */ | |
203 | + } | |
204 | +}; | |
205 | + | |
206 | +static int rad_panel_push_cmd_list(struct mipi_dsi_device *device) | |
207 | +{ | |
208 | + size_t i; | |
209 | + const u8 *cmd; | |
210 | + size_t count = sizeof(manufacturer_cmd_set) / CMD_TABLE_LEN; | |
211 | + int ret = 0; | |
212 | + | |
213 | + for (i = 0; i < count ; i++) { | |
214 | + cmd = manufacturer_cmd_set[i]; | |
215 | + ret = mipi_dsi_generic_write(device, cmd, CMD_TABLE_LEN); | |
216 | + if (ret < 0) | |
217 | + return ret; | |
218 | + } | |
219 | + | |
220 | + return ret; | |
221 | +}; | |
222 | + | |
223 | +static int rm67191_enable(struct udevice *dev) | |
224 | +{ | |
225 | + struct rm67191_panel_priv *priv = dev_get_priv(dev); | |
226 | + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); | |
227 | + struct mipi_dsi_device *dsi = plat->device; | |
228 | + u8 color_format = color_format_from_dsi_format(priv->format); | |
229 | + u16 brightness; | |
230 | + int ret; | |
231 | + | |
232 | + dsi->mode_flags |= MIPI_DSI_MODE_LPM; | |
233 | + | |
234 | + ret = rad_panel_push_cmd_list(dsi); | |
235 | + if (ret < 0) { | |
236 | + printf("Failed to send MCS (%d)\n", ret); | |
237 | + return -EIO; | |
238 | + } | |
239 | + | |
240 | + /* Select User Command Set table (CMD1) */ | |
241 | + ret = mipi_dsi_generic_write(dsi, (u8[]){ WRMAUCCTR, 0x00 }, 2); | |
242 | + if (ret < 0) | |
243 | + return -EIO; | |
244 | + | |
245 | + /* Software reset */ | |
246 | + ret = mipi_dsi_dcs_soft_reset(dsi); | |
247 | + if (ret < 0) { | |
248 | + printf("Failed to do Software Reset (%d)\n", ret); | |
249 | + return -EIO; | |
250 | + } | |
251 | + | |
252 | + /* Wait 80ms for panel out of reset */ | |
253 | + mdelay(80); | |
254 | + | |
255 | + /* Set DSI mode */ | |
256 | + ret = mipi_dsi_generic_write(dsi, (u8[]){ 0xC2, 0x0B }, 2); | |
257 | + if (ret < 0) { | |
258 | + printf("Failed to set DSI mode (%d)\n", ret); | |
259 | + return -EIO; | |
260 | + } | |
261 | + | |
262 | + /* Set tear ON */ | |
263 | + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); | |
264 | + if (ret < 0) { | |
265 | + printf("Failed to set tear ON (%d)\n", ret); | |
266 | + return -EIO; | |
267 | + } | |
268 | + | |
269 | + /* Set tear scanline */ | |
270 | + ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0x380); | |
271 | + if (ret < 0) { | |
272 | + printf("Failed to set tear scanline (%d)\n", ret); | |
273 | + return -EIO; | |
274 | + } | |
275 | + | |
276 | + /* Set pixel format */ | |
277 | + ret = mipi_dsi_dcs_set_pixel_format(dsi, color_format); | |
278 | + if (ret < 0) { | |
279 | + printf("Failed to set pixel format (%d)\n", ret); | |
280 | + return -EIO; | |
281 | + } | |
282 | + | |
283 | + | |
284 | + /* Set display brightness */ | |
285 | + brightness = 255; /* Max brightness */ | |
286 | + ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, &brightness, 2); | |
287 | + if (ret < 0) { | |
288 | + printf("Failed to set display brightness (%d)\n", | |
289 | + ret); | |
290 | + return -EIO; | |
291 | + } | |
292 | + | |
293 | + /* Exit sleep mode */ | |
294 | + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); | |
295 | + if (ret < 0) { | |
296 | + printf("Failed to exit sleep mode (%d)\n", ret); | |
297 | + return -EIO; | |
298 | + } | |
299 | + | |
300 | + mdelay(5); | |
301 | + | |
302 | + ret = mipi_dsi_dcs_set_display_on(dsi); | |
303 | + if (ret < 0) { | |
304 | + printf("Failed to set display ON (%d)\n", ret); | |
305 | + return -EIO; | |
306 | + } | |
307 | + | |
308 | + return 0; | |
309 | +} | |
310 | + | |
311 | +static int rm67191_panel_enable_backlight(struct udevice *dev) | |
312 | +{ | |
313 | + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); | |
314 | + struct mipi_dsi_device *device = plat->device; | |
315 | + int ret; | |
316 | + | |
317 | + ret = mipi_dsi_attach(device); | |
318 | + if (ret < 0) | |
319 | + return ret; | |
320 | + | |
321 | + return rm67191_enable(dev); | |
322 | +} | |
323 | + | |
324 | +static int rm67191_panel_get_display_timing(struct udevice *dev, | |
325 | + struct display_timing *timings) | |
326 | +{ | |
327 | + struct mipi_dsi_panel_plat *plat = dev_get_platdata(dev); | |
328 | + struct mipi_dsi_device *device = plat->device; | |
329 | + struct rm67191_panel_priv *priv = dev_get_priv(dev); | |
330 | + | |
331 | + memcpy(timings, &default_timing, sizeof(*timings)); | |
332 | + | |
333 | + /* fill characteristics of DSI data link */ | |
334 | + if (device) { | |
335 | + device->lanes = priv->lanes; | |
336 | + device->format = priv->format; | |
337 | + device->mode_flags = priv->mode_flags; | |
338 | + } | |
339 | + | |
340 | + return 0; | |
341 | +} | |
342 | + | |
343 | +static int rm67191_panel_probe(struct udevice *dev) | |
344 | +{ | |
345 | + struct rm67191_panel_priv *priv = dev_get_priv(dev); | |
346 | + int ret; | |
347 | + u32 video_mode; | |
348 | + | |
349 | + priv->format = MIPI_DSI_FMT_RGB888; | |
350 | + priv->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO; | |
351 | + | |
352 | + ret = dev_read_u32(dev, "video-mode", &video_mode); | |
353 | + if (!ret) { | |
354 | + switch (video_mode) { | |
355 | + case 0: | |
356 | + /* burst mode */ | |
357 | + priv->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST; | |
358 | + break; | |
359 | + case 1: | |
360 | + /* non-burst mode with sync event */ | |
361 | + break; | |
362 | + case 2: | |
363 | + /* non-burst mode with sync pulse */ | |
364 | + priv->mode_flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE; | |
365 | + break; | |
366 | + default: | |
367 | + dev_warn(dev, "invalid video mode %d\n", video_mode); | |
368 | + break; | |
369 | + } | |
370 | + } | |
371 | + | |
372 | + ret = dev_read_u32(dev, "dsi-lanes", &priv->lanes); | |
373 | + if (ret) { | |
374 | + printf("Failed to get dsi-lanes property (%d)\n", ret); | |
375 | + return ret; | |
376 | + } | |
377 | + | |
378 | + ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset, | |
379 | + GPIOD_IS_OUT); | |
380 | + if (ret) { | |
381 | + printf("Warning: cannot get reset GPIO\n"); | |
382 | + if (ret != -ENOENT) | |
383 | + return ret; | |
384 | + } | |
385 | + | |
386 | + /* reset panel */ | |
387 | + ret = dm_gpio_set_value(&priv->reset, true); | |
388 | + if (ret) | |
389 | + printf("reset gpio fails to set true\n"); | |
390 | + mdelay(100); | |
391 | + ret = dm_gpio_set_value(&priv->reset, false); | |
392 | + if (ret) | |
393 | + printf("reset gpio fails to set true\n"); | |
394 | + mdelay(100); | |
395 | + | |
396 | + return 0; | |
397 | +} | |
398 | + | |
399 | +static int rm67191_panel_disable(struct udevice *dev) | |
400 | +{ | |
401 | + struct rm67191_panel_priv *priv = dev_get_priv(dev); | |
402 | + | |
403 | + dm_gpio_set_value(&priv->reset, true); | |
404 | + | |
405 | + return 0; | |
406 | +} | |
407 | + | |
408 | +static const struct panel_ops rm67191_panel_ops = { | |
409 | + .enable_backlight = rm67191_panel_enable_backlight, | |
410 | + .get_display_timing = rm67191_panel_get_display_timing, | |
411 | +}; | |
412 | + | |
413 | +static const struct udevice_id rm67191_panel_ids[] = { | |
414 | + { .compatible = "raydium,rm67191" }, | |
415 | + { } | |
416 | +}; | |
417 | + | |
418 | +U_BOOT_DRIVER(rm67191_panel) = { | |
419 | + .name = "rm67191_panel", | |
420 | + .id = UCLASS_PANEL, | |
421 | + .of_match = rm67191_panel_ids, | |
422 | + .ops = &rm67191_panel_ops, | |
423 | + .probe = rm67191_panel_probe, | |
424 | + .remove = rm67191_panel_disable, | |
425 | + .platdata_auto_alloc_size = sizeof(struct mipi_dsi_panel_plat), | |
426 | + .priv_auto_alloc_size = sizeof(struct rm67191_panel_priv), | |
427 | +}; |