1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2016, The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/of_graph.h>
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include "adv7511.h"
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun static const struct reg_sequence adv7533_fixed_registers[] = {
11*4882a593Smuzhiyun { 0x16, 0x20 },
12*4882a593Smuzhiyun { 0x9a, 0xe0 },
13*4882a593Smuzhiyun { 0xba, 0x70 },
14*4882a593Smuzhiyun { 0xde, 0x82 },
15*4882a593Smuzhiyun { 0xe4, 0x40 },
16*4882a593Smuzhiyun { 0xe5, 0x80 },
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun static const struct reg_sequence adv7533_cec_fixed_registers[] = {
20*4882a593Smuzhiyun { 0x15, 0xd0 },
21*4882a593Smuzhiyun { 0x17, 0xd0 },
22*4882a593Smuzhiyun { 0x24, 0x20 },
23*4882a593Smuzhiyun { 0x57, 0x11 },
24*4882a593Smuzhiyun { 0x05, 0xc8 },
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun
adv7511_dsi_config_timing_gen(struct adv7511 * adv)27*4882a593Smuzhiyun static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun struct mipi_dsi_device *dsi = adv->dsi;
30*4882a593Smuzhiyun struct drm_display_mode *mode = &adv->curr_mode;
31*4882a593Smuzhiyun unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
32*4882a593Smuzhiyun u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun hsw = mode->hsync_end - mode->hsync_start;
35*4882a593Smuzhiyun hfp = mode->hsync_start - mode->hdisplay;
36*4882a593Smuzhiyun hbp = mode->htotal - mode->hsync_end;
37*4882a593Smuzhiyun vsw = mode->vsync_end - mode->vsync_start;
38*4882a593Smuzhiyun vfp = mode->vsync_start - mode->vdisplay;
39*4882a593Smuzhiyun vbp = mode->vtotal - mode->vsync_end;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun /* set pixel clock divider mode */
42*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x16,
43*4882a593Smuzhiyun clock_div_by_lanes[dsi->lanes - 2] << 3);
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* horizontal porch params */
46*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4);
47*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
48*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2a, hsw >> 4);
49*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff);
50*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2c, hfp >> 4);
51*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff);
52*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2e, hbp >> 4);
53*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun /* vertical porch params */
56*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4);
57*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
58*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x32, vsw >> 4);
59*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff);
60*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x34, vfp >> 4);
61*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff);
62*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x36, vbp >> 4);
63*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
adv7533_dsi_power_on(struct adv7511 * adv)66*4882a593Smuzhiyun void adv7533_dsi_power_on(struct adv7511 *adv)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun struct mipi_dsi_device *dsi = adv->dsi;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun if (adv->use_timing_gen)
71*4882a593Smuzhiyun adv7511_dsi_config_timing_gen(adv);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /* set number of dsi lanes */
74*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun if (adv->use_timing_gen) {
77*4882a593Smuzhiyun /* reset internal timing generator */
78*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x27, 0xcb);
79*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x27, 0x8b);
80*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x27, 0xcb);
81*4882a593Smuzhiyun } else {
82*4882a593Smuzhiyun /* disable internal timing generator */
83*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x27, 0x0b);
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun /* enable hdmi */
87*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x03, 0x89);
88*4882a593Smuzhiyun /* disable test mode */
89*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x55, 0x00);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers,
92*4882a593Smuzhiyun ARRAY_SIZE(adv7533_cec_fixed_registers));
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
adv7533_dsi_power_off(struct adv7511 * adv)95*4882a593Smuzhiyun void adv7533_dsi_power_off(struct adv7511 *adv)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun /* disable hdmi */
98*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x03, 0x0b);
99*4882a593Smuzhiyun /* disable internal timing generator */
100*4882a593Smuzhiyun regmap_write(adv->regmap_cec, 0x27, 0x0b);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
adv7533_mode_set(struct adv7511 * adv,const struct drm_display_mode * mode)103*4882a593Smuzhiyun void adv7533_mode_set(struct adv7511 *adv, const struct drm_display_mode *mode)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct mipi_dsi_device *dsi = adv->dsi;
106*4882a593Smuzhiyun int lanes, ret;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun if (adv->num_dsi_lanes != 4)
109*4882a593Smuzhiyun return;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun if (mode->clock > 80000)
112*4882a593Smuzhiyun lanes = 4;
113*4882a593Smuzhiyun else
114*4882a593Smuzhiyun lanes = 3;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (lanes != dsi->lanes) {
117*4882a593Smuzhiyun mipi_dsi_detach(dsi);
118*4882a593Smuzhiyun dsi->lanes = lanes;
119*4882a593Smuzhiyun ret = mipi_dsi_attach(dsi);
120*4882a593Smuzhiyun if (ret)
121*4882a593Smuzhiyun dev_err(&dsi->dev, "failed to change host lanes\n");
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
adv7533_patch_registers(struct adv7511 * adv)125*4882a593Smuzhiyun int adv7533_patch_registers(struct adv7511 *adv)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun return regmap_register_patch(adv->regmap,
128*4882a593Smuzhiyun adv7533_fixed_registers,
129*4882a593Smuzhiyun ARRAY_SIZE(adv7533_fixed_registers));
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
adv7533_patch_cec_registers(struct adv7511 * adv)132*4882a593Smuzhiyun int adv7533_patch_cec_registers(struct adv7511 *adv)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun return regmap_register_patch(adv->regmap_cec,
135*4882a593Smuzhiyun adv7533_cec_fixed_registers,
136*4882a593Smuzhiyun ARRAY_SIZE(adv7533_cec_fixed_registers));
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
adv7533_attach_dsi(struct adv7511 * adv)139*4882a593Smuzhiyun int adv7533_attach_dsi(struct adv7511 *adv)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun struct device *dev = &adv->i2c_main->dev;
142*4882a593Smuzhiyun struct mipi_dsi_host *host;
143*4882a593Smuzhiyun struct mipi_dsi_device *dsi;
144*4882a593Smuzhiyun int ret = 0;
145*4882a593Smuzhiyun const struct mipi_dsi_device_info info = { .type = "adv7533",
146*4882a593Smuzhiyun .channel = 0,
147*4882a593Smuzhiyun .node = NULL,
148*4882a593Smuzhiyun };
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun host = of_find_mipi_dsi_host_by_node(adv->host_node);
151*4882a593Smuzhiyun if (!host) {
152*4882a593Smuzhiyun dev_err(dev, "failed to find dsi host\n");
153*4882a593Smuzhiyun return -EPROBE_DEFER;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun dsi = mipi_dsi_device_register_full(host, &info);
157*4882a593Smuzhiyun if (IS_ERR(dsi)) {
158*4882a593Smuzhiyun dev_err(dev, "failed to create dsi device\n");
159*4882a593Smuzhiyun ret = PTR_ERR(dsi);
160*4882a593Smuzhiyun goto err_dsi_device;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun adv->dsi = dsi;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun dsi->lanes = adv->num_dsi_lanes;
166*4882a593Smuzhiyun dsi->format = MIPI_DSI_FMT_RGB888;
167*4882a593Smuzhiyun dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
168*4882a593Smuzhiyun MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun ret = mipi_dsi_attach(dsi);
171*4882a593Smuzhiyun if (ret < 0) {
172*4882a593Smuzhiyun dev_err(dev, "failed to attach dsi to host\n");
173*4882a593Smuzhiyun goto err_dsi_attach;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun return 0;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun err_dsi_attach:
179*4882a593Smuzhiyun mipi_dsi_device_unregister(dsi);
180*4882a593Smuzhiyun err_dsi_device:
181*4882a593Smuzhiyun return ret;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
adv7533_detach_dsi(struct adv7511 * adv)184*4882a593Smuzhiyun void adv7533_detach_dsi(struct adv7511 *adv)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun mipi_dsi_detach(adv->dsi);
187*4882a593Smuzhiyun mipi_dsi_device_unregister(adv->dsi);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
adv7533_parse_dt(struct device_node * np,struct adv7511 * adv)190*4882a593Smuzhiyun int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun u32 num_lanes;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun if (num_lanes < 1 || num_lanes > 4)
197*4882a593Smuzhiyun return -EINVAL;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun adv->num_dsi_lanes = num_lanes;
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun adv->host_node = of_graph_get_remote_node(np, 0, 0);
202*4882a593Smuzhiyun if (!adv->host_node)
203*4882a593Smuzhiyun return -ENODEV;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun of_node_put(adv->host_node);
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun adv->use_timing_gen = !of_property_read_bool(np,
208*4882a593Smuzhiyun "adi,disable-timing-generator");
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun /* TODO: Check if these need to be parsed by DT or not */
211*4882a593Smuzhiyun adv->rgb = true;
212*4882a593Smuzhiyun adv->embedded_sync = false;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun return 0;
215*4882a593Smuzhiyun }
216