Skip to content

Commit 1c66496

Browse files
Kevin Tangmlankhorst
authored andcommitted
drm/sprd: add Unisoc's drm mipi dsi&dphy driver
Adds dsi host controller support for the Unisoc's display subsystem. Adds dsi phy support for the Unisoc's display subsystem. Only MIPI DSI Displays supported, DP/TV/HMDI will be support in the feature. v1: - Remove dphy and dsi graph binding, merge the dphy driver into the dsi. v2: - Use drm_xxx to replace all DRM_XXX. - Use kzalloc to replace devm_kzalloc for sprd_dsi structure init. v4: - Use drmm_helpers to allocate encoder. - Move allocate encoder and connector to bind function. v5: - Drop the dsi ip file prefix. - Fix the checkpatch warnings. - Add Signed-off-by for dsi&dphy patch. - Use the mode_flags of mipi_dsi_device to setup crtc DPI and EDPI mode. v6: - Redesign the way to access the dsi register. - Reduce the dsi_context member variables. v7: - Fix codeing style issue by checkpatch. - Drop the pll registers structure define. - Use bridge API instead of drm panel API. - Register mipi_dsi_host on probe phase; - Remove some unused function. v8: - Fix missing signed-off-by. - Move component_add to dsi_host.attach callback. Cc: Orson Zhai <[email protected]> Cc: Chunyan Zhang <[email protected]> Signed-off-by: Kevin Tang <[email protected]> Signed-off-by: Maarten Lankhorst <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 2295bbd commit 1c66496

File tree

8 files changed

+1526
-2
lines changed

8 files changed

+1526
-2
lines changed

drivers/gpu/drm/sprd/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ config DRM_SPRD
55
select DRM_GEM_CMA_HELPER
66
select DRM_KMS_CMA_HELPER
77
select DRM_KMS_HELPER
8+
select DRM_MIPI_DSI
89
select VIDEOMODE_HELPERS
910
help
1011
Choose this option if you have a Unisoc chipset.

drivers/gpu/drm/sprd/Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# SPDX-License-Identifier: GPL-2.0
22

3-
obj-y := sprd_drm.o \
4-
sprd_dpu.o
3+
sprd-drm-y := sprd_drm.o \
4+
sprd_dpu.o \
5+
sprd_dsi.o \
6+
megacores_pll.o
7+
8+
obj-$(CONFIG_DRM_SPRD) += sprd-drm.o

drivers/gpu/drm/sprd/megacores_pll.c

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2020 Unisoc Inc.
4+
*/
5+
6+
#include <asm/div64.h>
7+
#include <linux/delay.h>
8+
#include <linux/init.h>
9+
#include <linux/kernel.h>
10+
#include <linux/regmap.h>
11+
#include <linux/string.h>
12+
13+
#include "sprd_dsi.h"
14+
15+
#define L 0
16+
#define H 1
17+
#define CLK 0
18+
#define DATA 1
19+
#define INFINITY 0xffffffff
20+
#define MIN_OUTPUT_FREQ (100)
21+
22+
#define AVERAGE(a, b) (min(a, b) + abs((b) - (a)) / 2)
23+
24+
/* sharkle */
25+
#define VCO_BAND_LOW 750
26+
#define VCO_BAND_MID 1100
27+
#define VCO_BAND_HIGH 1500
28+
#define PHY_REF_CLK 26000
29+
30+
static int dphy_calc_pll_param(struct dphy_pll *pll)
31+
{
32+
const u32 khz = 1000;
33+
const u32 mhz = 1000000;
34+
const unsigned long long factor = 100;
35+
unsigned long long tmp;
36+
int i;
37+
38+
pll->potential_fvco = pll->freq / khz;
39+
pll->ref_clk = PHY_REF_CLK / khz;
40+
41+
for (i = 0; i < 4; ++i) {
42+
if (pll->potential_fvco >= VCO_BAND_LOW &&
43+
pll->potential_fvco <= VCO_BAND_HIGH) {
44+
pll->fvco = pll->potential_fvco;
45+
pll->out_sel = BIT(i);
46+
break;
47+
}
48+
pll->potential_fvco <<= 1;
49+
}
50+
if (pll->fvco == 0)
51+
return -EINVAL;
52+
53+
if (pll->fvco >= VCO_BAND_LOW && pll->fvco <= VCO_BAND_MID) {
54+
/* vco band control */
55+
pll->vco_band = 0x0;
56+
/* low pass filter control */
57+
pll->lpf_sel = 1;
58+
} else if (pll->fvco > VCO_BAND_MID && pll->fvco <= VCO_BAND_HIGH) {
59+
pll->vco_band = 0x1;
60+
pll->lpf_sel = 0;
61+
} else {
62+
return -EINVAL;
63+
}
64+
65+
pll->nint = pll->fvco / pll->ref_clk;
66+
tmp = pll->fvco * factor * mhz;
67+
do_div(tmp, pll->ref_clk);
68+
tmp = tmp - pll->nint * factor * mhz;
69+
tmp *= BIT(20);
70+
do_div(tmp, 100000000);
71+
pll->kint = (u32)tmp;
72+
pll->refin = 3; /* pre-divider bypass */
73+
pll->sdm_en = true; /* use fraction N PLL */
74+
pll->fdk_s = 0x1; /* fraction */
75+
pll->cp_s = 0x0;
76+
pll->det_delay = 0x1;
77+
78+
return 0;
79+
}
80+
81+
static void dphy_set_pll_reg(struct dphy_pll *pll, struct regmap *regmap)
82+
{
83+
u8 reg_val[9] = {0};
84+
int i;
85+
86+
u8 reg_addr[] = {
87+
0x03, 0x04, 0x06, 0x08, 0x09,
88+
0x0a, 0x0b, 0x0e, 0x0f
89+
};
90+
91+
reg_val[0] = 1 | (1 << 1) | (pll->lpf_sel << 2);
92+
reg_val[1] = pll->div | (1 << 3) | (pll->cp_s << 5) | (pll->fdk_s << 7);
93+
reg_val[2] = pll->nint;
94+
reg_val[3] = pll->vco_band | (pll->sdm_en << 1) | (pll->refin << 2);
95+
reg_val[4] = pll->kint >> 12;
96+
reg_val[5] = pll->kint >> 4;
97+
reg_val[6] = pll->out_sel | ((pll->kint << 4) & 0xf);
98+
reg_val[7] = 1 << 4;
99+
reg_val[8] = pll->det_delay;
100+
101+
for (i = 0; i < sizeof(reg_addr); ++i) {
102+
regmap_write(regmap, reg_addr[i], reg_val[i]);
103+
DRM_DEBUG("%02x: %02x\n", reg_addr[i], reg_val[i]);
104+
}
105+
}
106+
107+
int dphy_pll_config(struct dsi_context *ctx)
108+
{
109+
struct sprd_dsi *dsi = container_of(ctx, struct sprd_dsi, ctx);
110+
struct regmap *regmap = ctx->regmap;
111+
struct dphy_pll *pll = &ctx->pll;
112+
int ret;
113+
114+
pll->freq = dsi->slave->hs_rate;
115+
116+
/* FREQ = 26M * (NINT + KINT / 2^20) / out_sel */
117+
ret = dphy_calc_pll_param(pll);
118+
if (ret) {
119+
drm_err(dsi->drm, "failed to calculate dphy pll parameters\n");
120+
return ret;
121+
}
122+
dphy_set_pll_reg(pll, regmap);
123+
124+
return 0;
125+
}
126+
127+
static void dphy_set_timing_reg(struct regmap *regmap, int type, u8 val[])
128+
{
129+
switch (type) {
130+
case REQUEST_TIME:
131+
regmap_write(regmap, 0x31, val[CLK]);
132+
regmap_write(regmap, 0x41, val[DATA]);
133+
regmap_write(regmap, 0x51, val[DATA]);
134+
regmap_write(regmap, 0x61, val[DATA]);
135+
regmap_write(regmap, 0x71, val[DATA]);
136+
137+
regmap_write(regmap, 0x90, val[CLK]);
138+
regmap_write(regmap, 0xa0, val[DATA]);
139+
regmap_write(regmap, 0xb0, val[DATA]);
140+
regmap_write(regmap, 0xc0, val[DATA]);
141+
regmap_write(regmap, 0xd0, val[DATA]);
142+
break;
143+
case PREPARE_TIME:
144+
regmap_write(regmap, 0x32, val[CLK]);
145+
regmap_write(regmap, 0x42, val[DATA]);
146+
regmap_write(regmap, 0x52, val[DATA]);
147+
regmap_write(regmap, 0x62, val[DATA]);
148+
regmap_write(regmap, 0x72, val[DATA]);
149+
150+
regmap_write(regmap, 0x91, val[CLK]);
151+
regmap_write(regmap, 0xa1, val[DATA]);
152+
regmap_write(regmap, 0xb1, val[DATA]);
153+
regmap_write(regmap, 0xc1, val[DATA]);
154+
regmap_write(regmap, 0xd1, val[DATA]);
155+
break;
156+
case ZERO_TIME:
157+
regmap_write(regmap, 0x33, val[CLK]);
158+
regmap_write(regmap, 0x43, val[DATA]);
159+
regmap_write(regmap, 0x53, val[DATA]);
160+
regmap_write(regmap, 0x63, val[DATA]);
161+
regmap_write(regmap, 0x73, val[DATA]);
162+
163+
regmap_write(regmap, 0x92, val[CLK]);
164+
regmap_write(regmap, 0xa2, val[DATA]);
165+
regmap_write(regmap, 0xb2, val[DATA]);
166+
regmap_write(regmap, 0xc2, val[DATA]);
167+
regmap_write(regmap, 0xd2, val[DATA]);
168+
break;
169+
case TRAIL_TIME:
170+
regmap_write(regmap, 0x34, val[CLK]);
171+
regmap_write(regmap, 0x44, val[DATA]);
172+
regmap_write(regmap, 0x54, val[DATA]);
173+
regmap_write(regmap, 0x64, val[DATA]);
174+
regmap_write(regmap, 0x74, val[DATA]);
175+
176+
regmap_write(regmap, 0x93, val[CLK]);
177+
regmap_write(regmap, 0xa3, val[DATA]);
178+
regmap_write(regmap, 0xb3, val[DATA]);
179+
regmap_write(regmap, 0xc3, val[DATA]);
180+
regmap_write(regmap, 0xd3, val[DATA]);
181+
break;
182+
case EXIT_TIME:
183+
regmap_write(regmap, 0x36, val[CLK]);
184+
regmap_write(regmap, 0x46, val[DATA]);
185+
regmap_write(regmap, 0x56, val[DATA]);
186+
regmap_write(regmap, 0x66, val[DATA]);
187+
regmap_write(regmap, 0x76, val[DATA]);
188+
189+
regmap_write(regmap, 0x95, val[CLK]);
190+
regmap_write(regmap, 0xA5, val[DATA]);
191+
regmap_write(regmap, 0xB5, val[DATA]);
192+
regmap_write(regmap, 0xc5, val[DATA]);
193+
regmap_write(regmap, 0xd5, val[DATA]);
194+
break;
195+
case CLKPOST_TIME:
196+
regmap_write(regmap, 0x35, val[CLK]);
197+
regmap_write(regmap, 0x94, val[CLK]);
198+
break;
199+
200+
/* the following just use default value */
201+
case SETTLE_TIME:
202+
fallthrough;
203+
case TA_GET:
204+
fallthrough;
205+
case TA_GO:
206+
fallthrough;
207+
case TA_SURE:
208+
fallthrough;
209+
default:
210+
break;
211+
}
212+
}
213+
214+
void dphy_timing_config(struct dsi_context *ctx)
215+
{
216+
struct regmap *regmap = ctx->regmap;
217+
struct dphy_pll *pll = &ctx->pll;
218+
const u32 factor = 2;
219+
const u32 scale = 100;
220+
u32 t_ui, t_byteck, t_half_byteck;
221+
u32 range[2], constant;
222+
u8 val[2];
223+
u32 tmp = 0;
224+
225+
/* t_ui: 1 ui, byteck: 8 ui, half byteck: 4 ui */
226+
t_ui = 1000 * scale / (pll->freq / 1000);
227+
t_byteck = t_ui << 3;
228+
t_half_byteck = t_ui << 2;
229+
constant = t_ui << 1;
230+
231+
/* REQUEST_TIME: HS T-LPX: LP-01
232+
* For T-LPX, mipi spec defined min value is 50ns,
233+
* but maybe it shouldn't be too small, because BTA,
234+
* LP-10, LP-00, LP-01, all of this is related to T-LPX.
235+
*/
236+
range[L] = 50 * scale;
237+
range[H] = INFINITY;
238+
val[CLK] = DIV_ROUND_UP(range[L] * (factor << 1), t_byteck) - 2;
239+
val[DATA] = val[CLK];
240+
dphy_set_timing_reg(regmap, REQUEST_TIME, val);
241+
242+
/* PREPARE_TIME: HS sequence: LP-00 */
243+
range[L] = 38 * scale;
244+
range[H] = 95 * scale;
245+
tmp = AVERAGE(range[L], range[H]);
246+
val[CLK] = DIV_ROUND_UP(AVERAGE(range[L], range[H]), t_half_byteck) - 1;
247+
range[L] = 40 * scale + 4 * t_ui;
248+
range[H] = 85 * scale + 6 * t_ui;
249+
tmp |= AVERAGE(range[L], range[H]) << 16;
250+
val[DATA] = DIV_ROUND_UP(AVERAGE(range[L], range[H]), t_half_byteck) - 1;
251+
dphy_set_timing_reg(regmap, PREPARE_TIME, val);
252+
253+
/* ZERO_TIME: HS-ZERO */
254+
range[L] = 300 * scale;
255+
range[H] = INFINITY;
256+
val[CLK] = DIV_ROUND_UP(range[L] * factor + (tmp & 0xffff)
257+
- 525 * t_byteck / 100, t_byteck) - 2;
258+
range[L] = 145 * scale + 10 * t_ui;
259+
val[DATA] = DIV_ROUND_UP(range[L] * factor
260+
+ ((tmp >> 16) & 0xffff) - 525 * t_byteck / 100,
261+
t_byteck) - 2;
262+
dphy_set_timing_reg(regmap, ZERO_TIME, val);
263+
264+
/* TRAIL_TIME: HS-TRAIL */
265+
range[L] = 60 * scale;
266+
range[H] = INFINITY;
267+
val[CLK] = DIV_ROUND_UP(range[L] * factor - constant, t_half_byteck);
268+
range[L] = max(8 * t_ui, 60 * scale + 4 * t_ui);
269+
val[DATA] = DIV_ROUND_UP(range[L] * 3 / 2 - constant, t_half_byteck) - 2;
270+
dphy_set_timing_reg(regmap, TRAIL_TIME, val);
271+
272+
/* EXIT_TIME: */
273+
range[L] = 100 * scale;
274+
range[H] = INFINITY;
275+
val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
276+
val[DATA] = val[CLK];
277+
dphy_set_timing_reg(regmap, EXIT_TIME, val);
278+
279+
/* CLKPOST_TIME: */
280+
range[L] = 60 * scale + 52 * t_ui;
281+
range[H] = INFINITY;
282+
val[CLK] = DIV_ROUND_UP(range[L] * factor, t_byteck) - 2;
283+
val[DATA] = val[CLK];
284+
dphy_set_timing_reg(regmap, CLKPOST_TIME, val);
285+
286+
/* SETTLE_TIME:
287+
* This time is used for receiver. So for transmitter,
288+
* it can be ignored.
289+
*/
290+
291+
/* TA_GO:
292+
* transmitter drives bridge state(LP-00) before releasing control,
293+
* reg 0x1f default value: 0x04, which is good.
294+
*/
295+
296+
/* TA_SURE:
297+
* After LP-10 state and before bridge state(LP-00),
298+
* reg 0x20 default value: 0x01, which is good.
299+
*/
300+
301+
/* TA_GET:
302+
* receiver drives Bridge state(LP-00) before releasing control
303+
* reg 0x21 default value: 0x03, which is good.
304+
*/
305+
}

drivers/gpu/drm/sprd/sprd_dpu.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "sprd_drm.h"
2727
#include "sprd_dpu.h"
28+
#include "sprd_dsi.h"
2829

2930
/* Global control registers */
3031
#define REG_DPU_CTRL 0x04
@@ -618,9 +619,21 @@ static void sprd_crtc_mode_set_nofb(struct drm_crtc *crtc)
618619
{
619620
struct sprd_dpu *dpu = to_sprd_crtc(crtc);
620621
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
622+
struct drm_encoder *encoder;
623+
struct sprd_dsi *dsi;
621624

622625
drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
623626

627+
drm_for_each_encoder_mask(encoder, crtc->dev,
628+
crtc->state->encoder_mask) {
629+
dsi = encoder_to_dsi(encoder);
630+
631+
if (dsi->slave->mode_flags & MIPI_DSI_MODE_VIDEO)
632+
dpu->ctx.if_type = SPRD_DPU_IF_DPI;
633+
else
634+
dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
635+
}
636+
624637
sprd_dpi_init(dpu);
625638
}
626639

drivers/gpu/drm/sprd/sprd_drm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ static struct platform_driver sprd_drm_driver = {
181181
static struct platform_driver *sprd_drm_drivers[] = {
182182
&sprd_drm_driver,
183183
&sprd_dpu_driver,
184+
&sprd_dsi_driver,
184185
};
185186

186187
static int __init sprd_drm_init(void)

drivers/gpu/drm/sprd/sprd_drm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ struct sprd_drm {
1414
};
1515

1616
extern struct platform_driver sprd_dpu_driver;
17+
extern struct platform_driver sprd_dsi_driver;
1718

1819
#endif /* _SPRD_DRM_H_ */

0 commit comments

Comments
 (0)