diff options
author | Dave Airlie <airlied@redhat.com> | 2014-04-05 02:09:15 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2014-04-05 02:09:15 -0400 |
commit | 13b938927412db95274a045aacadf310e6a30acb (patch) | |
tree | 028027f5dbbb374971c5f14dc4443a5b8867e08c | |
parent | 14c6d5bdf759274868c6a3534e56f1991118df63 (diff) | |
parent | 96e112c44477edea1c01fbb976205e751f4229b9 (diff) |
Merge branch 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
Summaries:
- Add MIPI-DSI Driver, and dt bindigs
- Add S6E8AA0 MIPI-DSI based panel drivers, and dt bindings
- Add LD9040 parallel panel driver
. this driver is placed in drivers/gpu/drm/panel, and it seems
to be used for exynos drm as of now,
- Some fixups
Changelog v2:
- Remove super device support, and relevant dt bindings for more reviews.
- Fix module build errors you pointed out.
- Re-based it to drm-next again.
* 'exynos-drm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/bridge: export ptn3460_init function
drm/exynos: remove MODULE_DEVICE_TABLE definitions
ARM: dts: exynos4412-trats2: enable exynos/fimd node
ARM: dts: exynos4210-trats: enable exynos/fimd node
ARM: dts: exynos4412-trats2: add panel node
ARM: dts: exynos4210-trats: add panel node
ARM: dts: exynos4: add MIPI DSI Master node
drm/panel: add S6E8AA0 driver
ARM: dts: exynos4210-universal_c210: add proper panel node
drm/panel: add ld9040 driver
panel/ld9040: add DT bindings
panel/s6e8aa0: add DT bindings
drm/exynos: add DSIM driver
exynos/dsim: add DT bindings
drm/exynos: disallow fbdev initialization if no device is connected
drm/mipi_dsi: create dsi devices only for nodes with reg property
drm/mipi_dsi: add flags to DSI messages
21 files changed, 3445 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/panel/samsung,ld9040.txt b/Documentation/devicetree/bindings/panel/samsung,ld9040.txt new file mode 100644 index 000000000000..07c36c3f7b52 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,ld9040.txt | |||
@@ -0,0 +1,66 @@ | |||
1 | Samsung LD9040 AMOLED LCD parallel RGB panel with SPI control bus | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "samsung,ld9040" | ||
5 | - reg: address of the panel on SPI bus | ||
6 | - vdd3-supply: core voltage supply | ||
7 | - vci-supply: voltage supply for analog circuits | ||
8 | - reset-gpios: a GPIO spec for the reset pin | ||
9 | - display-timings: timings for the connected panel according to [1] | ||
10 | |||
11 | The panel must obey rules for SPI slave device specified in document [2]. | ||
12 | |||
13 | Optional properties: | ||
14 | - power-on-delay: delay after turning regulators on [ms] | ||
15 | - reset-delay: delay after reset sequence [ms] | ||
16 | - panel-width-mm: physical panel width [mm] | ||
17 | - panel-height-mm: physical panel height [mm] | ||
18 | |||
19 | The device node can contain one 'port' child node with one child | ||
20 | 'endpoint' node, according to the bindings defined in [3]. This | ||
21 | node should describe panel's video bus. | ||
22 | |||
23 | [1]: Documentation/devicetree/bindings/video/display-timing.txt | ||
24 | [2]: Documentation/devicetree/bindings/spi/spi-bus.txt | ||
25 | [3]: Documentation/devicetree/bindings/media/video-interfaces.txt | ||
26 | |||
27 | Example: | ||
28 | |||
29 | lcd@0 { | ||
30 | compatible = "samsung,ld9040"; | ||
31 | reg = <0>; | ||
32 | vdd3-supply = <&ldo7_reg>; | ||
33 | vci-supply = <&ldo17_reg>; | ||
34 | reset-gpios = <&gpy4 5 0>; | ||
35 | spi-max-frequency = <1200000>; | ||
36 | spi-cpol; | ||
37 | spi-cpha; | ||
38 | power-on-delay = <10>; | ||
39 | reset-delay = <10>; | ||
40 | panel-width-mm = <90>; | ||
41 | panel-height-mm = <154>; | ||
42 | |||
43 | display-timings { | ||
44 | timing { | ||
45 | clock-frequency = <23492370>; | ||
46 | hactive = <480>; | ||
47 | vactive = <800>; | ||
48 | hback-porch = <16>; | ||
49 | hfront-porch = <16>; | ||
50 | vback-porch = <2>; | ||
51 | vfront-porch = <28>; | ||
52 | hsync-len = <2>; | ||
53 | vsync-len = <1>; | ||
54 | hsync-active = <0>; | ||
55 | vsync-active = <0>; | ||
56 | de-active = <0>; | ||
57 | pixelclk-active = <0>; | ||
58 | }; | ||
59 | }; | ||
60 | |||
61 | port { | ||
62 | lcd_ep: endpoint { | ||
63 | remote-endpoint = <&fimd_dpi_ep>; | ||
64 | }; | ||
65 | }; | ||
66 | }; | ||
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e8aa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e8aa0.txt new file mode 100644 index 000000000000..e7ee988e3156 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e8aa0.txt | |||
@@ -0,0 +1,56 @@ | |||
1 | Samsung S6E8AA0 AMOLED LCD 5.3 inch panel | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "samsung,s6e8aa0" | ||
5 | - reg: the virtual channel number of a DSI peripheral | ||
6 | - vdd3-supply: core voltage supply | ||
7 | - vci-supply: voltage supply for analog circuits | ||
8 | - reset-gpios: a GPIO spec for the reset pin | ||
9 | - display-timings: timings for the connected panel as described by [1] | ||
10 | |||
11 | Optional properties: | ||
12 | - power-on-delay: delay after turning regulators on [ms] | ||
13 | - reset-delay: delay after reset sequence [ms] | ||
14 | - init-delay: delay after initialization sequence [ms] | ||
15 | - panel-width-mm: physical panel width [mm] | ||
16 | - panel-height-mm: physical panel height [mm] | ||
17 | - flip-horizontal: boolean to flip image horizontally | ||
18 | - flip-vertical: boolean to flip image vertically | ||
19 | |||
20 | The device node can contain one 'port' child node with one child | ||
21 | 'endpoint' node, according to the bindings defined in [2]. This | ||
22 | node should describe panel's video bus. | ||
23 | |||
24 | [1]: Documentation/devicetree/bindings/video/display-timing.txt | ||
25 | [2]: Documentation/devicetree/bindings/media/video-interfaces.txt | ||
26 | |||
27 | Example: | ||
28 | |||
29 | panel { | ||
30 | compatible = "samsung,s6e8aa0"; | ||
31 | reg = <0>; | ||
32 | vdd3-supply = <&vcclcd_reg>; | ||
33 | vci-supply = <&vlcd_reg>; | ||
34 | reset-gpios = <&gpy4 5 0>; | ||
35 | power-on-delay= <50>; | ||
36 | reset-delay = <100>; | ||
37 | init-delay = <100>; | ||
38 | panel-width-mm = <58>; | ||
39 | panel-height-mm = <103>; | ||
40 | flip-horizontal; | ||
41 | flip-vertical; | ||
42 | |||
43 | display-timings { | ||
44 | timing0: timing-0 { | ||
45 | clock-frequency = <57153600>; | ||
46 | hactive = <720>; | ||
47 | vactive = <1280>; | ||
48 | hfront-porch = <5>; | ||
49 | hback-porch = <5>; | ||
50 | hsync-len = <5>; | ||
51 | vfront-porch = <13>; | ||
52 | vback-porch = <1>; | ||
53 | vsync-len = <2>; | ||
54 | }; | ||
55 | }; | ||
56 | }; | ||
diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt new file mode 100644 index 000000000000..33b5730d07ba --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt | |||
@@ -0,0 +1,80 @@ | |||
1 | Exynos MIPI DSI Master | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: "samsung,exynos4210-mipi-dsi" | ||
5 | - reg: physical base address and length of the registers set for the device | ||
6 | - interrupts: should contain DSI interrupt | ||
7 | - clocks: list of clock specifiers, must contain an entry for each required | ||
8 | entry in clock-names | ||
9 | - clock-names: should include "bus_clk"and "pll_clk" entries | ||
10 | - phys: list of phy specifiers, must contain an entry for each required | ||
11 | entry in phy-names | ||
12 | - phy-names: should include "dsim" entry | ||
13 | - vddcore-supply: MIPI DSIM Core voltage supply (e.g. 1.1V) | ||
14 | - vddio-supply: MIPI DSIM I/O and PLL voltage supply (e.g. 1.8V) | ||
15 | - samsung,pll-clock-frequency: specifies frequency of the "pll_clk" clock | ||
16 | - #address-cells, #size-cells: should be set respectively to <1> and <0> | ||
17 | according to DSI host bindings (see MIPI DSI bindings [1]) | ||
18 | |||
19 | Optional properties: | ||
20 | - samsung,power-domain: a phandle to DSIM power domain node | ||
21 | |||
22 | Child nodes: | ||
23 | Should contain DSI peripheral nodes (see MIPI DSI bindings [1]). | ||
24 | |||
25 | Video interfaces: | ||
26 | Device node can contain video interface port nodes according to [2]. | ||
27 | The following are properties specific to those nodes: | ||
28 | |||
29 | port node: | ||
30 | - reg: (required) can be 0 for input RGB/I80 port or 1 for DSI port; | ||
31 | |||
32 | endpoint node of DSI port (reg = 1): | ||
33 | - samsung,burst-clock-frequency: specifies DSI frequency in high-speed burst | ||
34 | mode | ||
35 | - samsung,esc-clock-frequency: specifies DSI frequency in escape mode | ||
36 | |||
37 | [1]: Documentation/devicetree/bindings/mipi/dsi/mipi-dsi-bus.txt | ||
38 | [2]: Documentation/devicetree/bindings/media/video-interfaces.txt | ||
39 | |||
40 | Example: | ||
41 | |||
42 | dsi@11C80000 { | ||
43 | compatible = "samsung,exynos4210-mipi-dsi"; | ||
44 | reg = <0x11C80000 0x10000>; | ||
45 | interrupts = <0 79 0>; | ||
46 | clocks = <&clock 286>, <&clock 143>; | ||
47 | clock-names = "bus_clk", "pll_clk"; | ||
48 | phys = <&mipi_phy 1>; | ||
49 | phy-names = "dsim"; | ||
50 | vddcore-supply = <&vusb_reg>; | ||
51 | vddio-supply = <&vmipi_reg>; | ||
52 | samsung,power-domain = <&pd_lcd0>; | ||
53 | #address-cells = <1>; | ||
54 | #size-cells = <0>; | ||
55 | samsung,pll-clock-frequency = <24000000>; | ||
56 | |||
57 | panel@1 { | ||
58 | reg = <0>; | ||
59 | ... | ||
60 | port { | ||
61 | panel_ep: endpoint { | ||
62 | remote-endpoint = <&dsi_ep>; | ||
63 | }; | ||
64 | }; | ||
65 | }; | ||
66 | |||
67 | ports { | ||
68 | #address-cells = <1>; | ||
69 | #size-cells = <0>; | ||
70 | |||
71 | port@1 { | ||
72 | dsi_ep: endpoint { | ||
73 | reg = <0>; | ||
74 | samsung,burst-clock-frequency = <500000000>; | ||
75 | samsung,esc-clock-frequency = <20000000>; | ||
76 | remote-endpoint = <&panel_ep>; | ||
77 | }; | ||
78 | }; | ||
79 | }; | ||
80 | }; | ||
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 08452e183b57..3d14cdb0776a 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi | |||
@@ -104,6 +104,20 @@ | |||
104 | reg = <0x10010000 0x400>; | 104 | reg = <0x10010000 0x400>; |
105 | }; | 105 | }; |
106 | 106 | ||
107 | dsi_0: dsi@11C80000 { | ||
108 | compatible = "samsung,exynos4210-mipi-dsi"; | ||
109 | reg = <0x11C80000 0x10000>; | ||
110 | interrupts = <0 79 0>; | ||
111 | samsung,power-domain = <&pd_lcd0>; | ||
112 | phys = <&mipi_phy 1>; | ||
113 | phy-names = "dsim"; | ||
114 | clocks = <&clock 286>, <&clock 143>; | ||
115 | clock-names = "bus_clk", "pll_clk"; | ||
116 | status = "disabled"; | ||
117 | #address-cells = <1>; | ||
118 | #size-cells = <0>; | ||
119 | }; | ||
120 | |||
107 | camera { | 121 | camera { |
108 | compatible = "samsung,fimc", "simple-bus"; | 122 | compatible = "samsung,fimc", "simple-bus"; |
109 | status = "disabled"; | 123 | status = "disabled"; |
diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index 63cc571ca307..02c6768f52b4 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts | |||
@@ -353,6 +353,67 @@ | |||
353 | }; | 353 | }; |
354 | }; | 354 | }; |
355 | 355 | ||
356 | dsi_0: dsi@11C80000 { | ||
357 | vddcore-supply = <&vusb_reg>; | ||
358 | vddio-supply = <&vmipi_reg>; | ||
359 | samsung,pll-clock-frequency = <24000000>; | ||
360 | status = "okay"; | ||
361 | |||
362 | ports { | ||
363 | #address-cells = <1>; | ||
364 | #size-cells = <0>; | ||
365 | |||
366 | port@1 { | ||
367 | reg = <1>; | ||
368 | |||
369 | dsi_out: endpoint { | ||
370 | remote-endpoint = <&dsi_in>; | ||
371 | samsung,burst-clock-frequency = <500000000>; | ||
372 | samsung,esc-clock-frequency = <20000000>; | ||
373 | }; | ||
374 | }; | ||
375 | }; | ||
376 | |||
377 | panel@0 { | ||
378 | reg = <0>; | ||
379 | compatible = "samsung,s6e8aa0"; | ||
380 | vdd3-supply = <&vcclcd_reg>; | ||
381 | vci-supply = <&vlcd_reg>; | ||
382 | reset-gpios = <&gpy4 5 0>; | ||
383 | power-on-delay= <50>; | ||
384 | reset-delay = <100>; | ||
385 | init-delay = <100>; | ||
386 | flip-horizontal; | ||
387 | flip-vertical; | ||
388 | panel-width-mm = <58>; | ||
389 | panel-height-mm = <103>; | ||
390 | |||
391 | display-timings { | ||
392 | timing-0 { | ||
393 | clock-frequency = <57153600>; | ||
394 | hactive = <720>; | ||
395 | vactive = <1280>; | ||
396 | hfront-porch = <5>; | ||
397 | hback-porch = <5>; | ||
398 | hsync-len = <5>; | ||
399 | vfront-porch = <13>; | ||
400 | vback-porch = <1>; | ||
401 | vsync-len = <2>; | ||
402 | }; | ||
403 | }; | ||
404 | |||
405 | port { | ||
406 | dsi_in: endpoint { | ||
407 | remote-endpoint = <&dsi_out>; | ||
408 | }; | ||
409 | }; | ||
410 | }; | ||
411 | }; | ||
412 | |||
413 | fimd@11c00000 { | ||
414 | status = "okay"; | ||
415 | }; | ||
416 | |||
356 | camera { | 417 | camera { |
357 | pinctrl-names = "default"; | 418 | pinctrl-names = "default"; |
358 | pinctrl-0 = <>; | 419 | pinctrl-0 = <>; |
diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts b/arch/arm/boot/dts/exynos4210-universal_c210.dts index 477208d98c6d..0a80a72368d3 100644 --- a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++ b/arch/arm/boot/dts/exynos4210-universal_c210.dts | |||
@@ -225,7 +225,6 @@ | |||
225 | regulator-name = "VLCD+VMIPI_1.8V"; | 225 | regulator-name = "VLCD+VMIPI_1.8V"; |
226 | regulator-min-microvolt = <1800000>; | 226 | regulator-min-microvolt = <1800000>; |
227 | regulator-max-microvolt = <1800000>; | 227 | regulator-max-microvolt = <1800000>; |
228 | regulator-always-on; | ||
229 | }; | 228 | }; |
230 | 229 | ||
231 | ldo8_reg: LDO8 { | 230 | ldo8_reg: LDO8 { |
@@ -289,7 +288,6 @@ | |||
289 | regulator-name = "VCC_3.0V_LCD"; | 288 | regulator-name = "VCC_3.0V_LCD"; |
290 | regulator-min-microvolt = <3000000>; | 289 | regulator-min-microvolt = <3000000>; |
291 | regulator-max-microvolt = <3000000>; | 290 | regulator-max-microvolt = <3000000>; |
292 | regulator-always-on; | ||
293 | }; | 291 | }; |
294 | 292 | ||
295 | buck1_reg: BUCK1 { | 293 | buck1_reg: BUCK1 { |
@@ -347,27 +345,66 @@ | |||
347 | }; | 345 | }; |
348 | }; | 346 | }; |
349 | 347 | ||
348 | spi-lcd { | ||
349 | compatible = "spi-gpio"; | ||
350 | #address-cells = <1>; | ||
351 | #size-cells = <0>; | ||
352 | |||
353 | gpio-sck = <&gpy3 1 0>; | ||
354 | gpio-mosi = <&gpy3 3 0>; | ||
355 | num-chipselects = <1>; | ||
356 | cs-gpios = <&gpy4 3 0>; | ||
357 | |||
358 | lcd@0 { | ||
359 | compatible = "samsung,ld9040"; | ||
360 | reg = <0>; | ||
361 | vdd3-supply = <&ldo7_reg>; | ||
362 | vci-supply = <&ldo17_reg>; | ||
363 | reset-gpios = <&gpy4 5 0>; | ||
364 | spi-max-frequency = <1200000>; | ||
365 | spi-cpol; | ||
366 | spi-cpha; | ||
367 | power-on-delay = <10>; | ||
368 | reset-delay = <10>; | ||
369 | panel-width-mm = <90>; | ||
370 | panel-height-mm = <154>; | ||
371 | display-timings { | ||
372 | timing { | ||
373 | clock-frequency = <23492370>; | ||
374 | hactive = <480>; | ||
375 | vactive = <800>; | ||
376 | hback-porch = <16>; | ||
377 | hfront-porch = <16>; | ||
378 | vback-porch = <2>; | ||
379 | vfront-porch = <28>; | ||
380 | hsync-len = <2>; | ||
381 | vsync-len = <1>; | ||
382 | hsync-active = <0>; | ||
383 | vsync-active = <0>; | ||
384 | de-active = <0>; | ||
385 | pixelclk-active = <0>; | ||
386 | }; | ||
387 | }; | ||
388 | port { | ||
389 | lcd_ep: endpoint { | ||
390 | remote-endpoint = <&fimd_dpi_ep>; | ||
391 | }; | ||
392 | }; | ||
393 | }; | ||
394 | }; | ||
395 | |||
350 | fimd: fimd@11c00000 { | 396 | fimd: fimd@11c00000 { |
351 | pinctrl-0 = <&lcd_clk>, <&lcd_data24>; | 397 | pinctrl-0 = <&lcd_clk>, <&lcd_data24>; |
352 | pinctrl-names = "default"; | 398 | pinctrl-names = "default"; |
353 | status = "okay"; | 399 | status = "okay"; |
354 | samsung,invert-vden; | 400 | samsung,invert-vden; |
355 | samsung,invert-vclk; | 401 | samsung,invert-vclk; |
356 | display-timings { | 402 | #address-cells = <1>; |
357 | timing { | 403 | #size-cells = <0>; |
358 | clock-frequency = <23492370>; | 404 | port@3 { |
359 | hactive = <480>; | 405 | reg = <3>; |
360 | vactive = <800>; | 406 | fimd_dpi_ep: endpoint { |
361 | hback-porch = <16>; | 407 | remote-endpoint = <&lcd_ep>; |
362 | hfront-porch = <16>; | ||
363 | vback-porch = <2>; | ||
364 | vfront-porch = <28>; | ||
365 | hsync-len = <2>; | ||
366 | vsync-len = <1>; | ||
367 | hsync-active = <0>; | ||
368 | vsync-active = <0>; | ||
369 | de-active = <0>; | ||
370 | pixelclk-active = <0>; | ||
371 | }; | 408 | }; |
372 | }; | 409 | }; |
373 | }; | 410 | }; |
diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts index 4f851ccf40eb..53c717b6eb69 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts +++ b/arch/arm/boot/dts/exynos4412-trats2.dts | |||
@@ -71,6 +71,15 @@ | |||
71 | enable-active-high; | 71 | enable-active-high; |
72 | }; | 72 | }; |
73 | 73 | ||
74 | lcd_vdd3_reg: voltage-regulator-2 { | ||
75 | compatible = "regulator-fixed"; | ||
76 | regulator-name = "LCD_VDD_2.2V"; | ||
77 | regulator-min-microvolt = <2200000>; | ||
78 | regulator-max-microvolt = <2200000>; | ||
79 | gpio = <&gpc0 1 0>; | ||
80 | enable-active-high; | ||
81 | }; | ||
82 | |||
74 | /* More to come */ | 83 | /* More to come */ |
75 | }; | 84 | }; |
76 | 85 | ||
@@ -511,6 +520,67 @@ | |||
511 | }; | 520 | }; |
512 | }; | 521 | }; |
513 | 522 | ||
523 | dsi_0: dsi@11C80000 { | ||
524 | vddcore-supply = <&ldo8_reg>; | ||
525 | vddio-supply = <&ldo10_reg>; | ||
526 | samsung,pll-clock-frequency = <24000000>; | ||
527 | status = "okay"; | ||
528 | |||
529 | ports { | ||
530 | #address-cells = <1>; | ||
531 | #size-cells = <0>; | ||
532 | |||
533 | port@1 { | ||
534 | reg = <1>; | ||
535 | |||
536 | dsi_out: endpoint { | ||
537 | remote-endpoint = <&dsi_in>; | ||
538 | samsung,burst-clock-frequency = <500000000>; | ||
539 | samsung,esc-clock-frequency = <20000000>; | ||
540 | }; | ||
541 | }; | ||
542 | }; | ||
543 | |||
544 | panel@0 { | ||
545 | compatible = "samsung,s6e8aa0"; | ||
546 | reg = <0>; | ||
547 | vdd3-supply = <&lcd_vdd3_reg>; | ||
548 | vci-supply = <&ldo25_reg>; | ||
549 | reset-gpios = <&gpy4 5 0>; | ||
550 | power-on-delay= <50>; | ||
551 | reset-delay = <100>; | ||
552 | init-delay = <100>; | ||
553 | flip-horizontal; | ||
554 | flip-vertical; | ||
555 | panel-width-mm = <58>; | ||
556 | panel-height-mm = <103>; | ||
557 | |||
558 | display-timings { | ||
559 | timing-0 { | ||
560 | clock-frequency = <0>; | ||
561 | hactive = <720>; | ||
562 | vactive = <1280>; | ||
563 | hfront-porch = <5>; | ||
564 | hback-porch = <5>; | ||
565 | hsync-len = <5>; | ||
566 | vfront-porch = <13>; | ||
567 | vback-porch = <1>; | ||
568 | vsync-len = <2>; | ||
569 | }; | ||
570 | }; | ||
571 | |||
572 | port { | ||
573 | dsi_in: endpoint { | ||
574 | remote-endpoint = <&dsi_out>; | ||
575 | }; | ||
576 | }; | ||
577 | }; | ||
578 | }; | ||
579 | |||
580 | fimd@11c00000 { | ||
581 | status = "okay"; | ||
582 | }; | ||
583 | |||
514 | camera { | 584 | camera { |
515 | pinctrl-0 = <&cam_port_b_clk_active>; | 585 | pinctrl-0 = <&cam_port_b_clk_active>; |
516 | pinctrl-names = "default"; | 586 | pinctrl-names = "default"; |
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index a9e5c1a13666..b171901a3553 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c | |||
@@ -347,3 +347,4 @@ err: | |||
347 | gpio_free(ptn_bridge->gpio_rst_n); | 347 | gpio_free(ptn_bridge->gpio_rst_n); |
348 | return ret; | 348 | return ret; |
349 | } | 349 | } |
350 | EXPORT_SYMBOL(ptn3460_init); | ||
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index b155ee2ffa17..09821f46d768 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c | |||
@@ -142,8 +142,12 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host) | |||
142 | { | 142 | { |
143 | struct device_node *node; | 143 | struct device_node *node; |
144 | 144 | ||
145 | for_each_available_child_of_node(host->dev->of_node, node) | 145 | for_each_available_child_of_node(host->dev->of_node, node) { |
146 | /* skip nodes without reg property */ | ||
147 | if (!of_find_property(node, "reg", NULL)) | ||
148 | continue; | ||
146 | of_mipi_dsi_device_add(host, node); | 149 | of_mipi_dsi_device_add(host, node); |
150 | } | ||
147 | 151 | ||
148 | return 0; | 152 | return 0; |
149 | } | 153 | } |
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 56f95811a5e5..5bf5bca94f56 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
@@ -39,6 +39,15 @@ config DRM_EXYNOS_DPI | |||
39 | help | 39 | help |
40 | This enables support for Exynos parallel output. | 40 | This enables support for Exynos parallel output. |
41 | 41 | ||
42 | config DRM_EXYNOS_DSI | ||
43 | bool "EXYNOS DRM MIPI-DSI driver support" | ||
44 | depends on DRM_EXYNOS | ||
45 | select DRM_MIPI_DSI | ||
46 | select DRM_PANEL | ||
47 | default n | ||
48 | help | ||
49 | This enables support for Exynos MIPI-DSI device. | ||
50 | |||
42 | config DRM_EXYNOS_DP | 51 | config DRM_EXYNOS_DP |
43 | bool "EXYNOS DRM DP driver support" | 52 | bool "EXYNOS DRM DP driver support" |
44 | depends on DRM_EXYNOS && ARCH_EXYNOS | 53 | depends on DRM_EXYNOS && ARCH_EXYNOS |
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index babcd52b65df..33ae3652b8da 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile | |||
@@ -12,6 +12,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IOMMU) += exynos_drm_iommu.o | |||
12 | exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o | 12 | exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o |
13 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o | 13 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o |
14 | exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o | 14 | exynosdrm-$(CONFIG_DRM_EXYNOS_DPI) += exynos_drm_dpi.o |
15 | exynosdrm-$(CONFIG_DRM_EXYNOS_DSI) += exynos_drm_dsi.o | ||
15 | exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o | 16 | exynosdrm-$(CONFIG_DRM_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o |
16 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o | 17 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o |
17 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o | 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o |
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index a59bca9f16e1..aed533bbfd31 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c | |||
@@ -1339,7 +1339,6 @@ static const struct of_device_id exynos_dp_match[] = { | |||
1339 | { .compatible = "samsung,exynos5-dp" }, | 1339 | { .compatible = "samsung,exynos5-dp" }, |
1340 | {}, | 1340 | {}, |
1341 | }; | 1341 | }; |
1342 | MODULE_DEVICE_TABLE(of, exynos_dp_match); | ||
1343 | 1342 | ||
1344 | struct platform_driver dp_driver = { | 1343 | struct platform_driver dp_driver = { |
1345 | .probe = exynos_dp_probe, | 1344 | .probe = exynos_dp_probe, |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 771c87e90a2f..2d27ba23a6a8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -450,6 +450,12 @@ static int __init exynos_drm_init(void) | |||
450 | goto out_dp; | 450 | goto out_dp; |
451 | #endif | 451 | #endif |
452 | 452 | ||
453 | #ifdef CONFIG_DRM_EXYNOS_DSI | ||
454 | ret = platform_driver_register(&dsi_driver); | ||
455 | if (ret < 0) | ||
456 | goto out_dsi; | ||
457 | #endif | ||
458 | |||
453 | #ifdef CONFIG_DRM_EXYNOS_FIMD | 459 | #ifdef CONFIG_DRM_EXYNOS_FIMD |
454 | ret = platform_driver_register(&fimd_driver); | 460 | ret = platform_driver_register(&fimd_driver); |
455 | if (ret < 0) | 461 | if (ret < 0) |
@@ -566,6 +572,11 @@ out_hdmi: | |||
566 | out_fimd: | 572 | out_fimd: |
567 | #endif | 573 | #endif |
568 | 574 | ||
575 | #ifdef CONFIG_DRM_EXYNOS_DSI | ||
576 | platform_driver_unregister(&dsi_driver); | ||
577 | out_dsi: | ||
578 | #endif | ||
579 | |||
569 | #ifdef CONFIG_DRM_EXYNOS_DP | 580 | #ifdef CONFIG_DRM_EXYNOS_DP |
570 | platform_driver_unregister(&dp_driver); | 581 | platform_driver_unregister(&dp_driver); |
571 | out_dp: | 582 | out_dp: |
@@ -613,6 +624,10 @@ static void __exit exynos_drm_exit(void) | |||
613 | platform_driver_unregister(&fimd_driver); | 624 | platform_driver_unregister(&fimd_driver); |
614 | #endif | 625 | #endif |
615 | 626 | ||
627 | #ifdef CONFIG_DRM_EXYNOS_DSI | ||
628 | platform_driver_unregister(&dsi_driver); | ||
629 | #endif | ||
630 | |||
616 | #ifdef CONFIG_DRM_EXYNOS_DP | 631 | #ifdef CONFIG_DRM_EXYNOS_DP |
617 | platform_driver_unregister(&dp_driver); | 632 | platform_driver_unregister(&dp_driver); |
618 | #endif | 633 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 2d892f32e831..4c5cf6843137 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -370,6 +370,7 @@ static inline int exynos_dpi_remove(struct device *dev) { return 0; } | |||
370 | #endif | 370 | #endif |
371 | 371 | ||
372 | extern struct platform_driver dp_driver; | 372 | extern struct platform_driver dp_driver; |
373 | extern struct platform_driver dsi_driver; | ||
373 | extern struct platform_driver fimd_driver; | 374 | extern struct platform_driver fimd_driver; |
374 | extern struct platform_driver hdmi_driver; | 375 | extern struct platform_driver hdmi_driver; |
375 | extern struct platform_driver mixer_driver; | 376 | extern struct platform_driver mixer_driver; |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c new file mode 100644 index 000000000000..eb73e3bf2a0c --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c | |||
@@ -0,0 +1,1524 @@ | |||
1 | /* | ||
2 | * Samsung SoC MIPI DSI Master driver. | ||
3 | * | ||
4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd | ||
5 | * | ||
6 | * Contacts: Tomasz Figa <t.figa@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <drm/drmP.h> | ||
14 | #include <drm/drm_crtc_helper.h> | ||
15 | #include <drm/drm_mipi_dsi.h> | ||
16 | #include <drm/drm_panel.h> | ||
17 | |||
18 | #include <linux/clk.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/phy/phy.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | |||
23 | #include <video/mipi_display.h> | ||
24 | #include <video/videomode.h> | ||
25 | |||
26 | #include "exynos_drm_drv.h" | ||
27 | |||
28 | /* returns true iff both arguments logically differs */ | ||
29 | #define NEQV(a, b) (!(a) ^ !(b)) | ||
30 | |||
31 | #define DSIM_STATUS_REG 0x0 /* Status register */ | ||
32 | #define DSIM_SWRST_REG 0x4 /* Software reset register */ | ||
33 | #define DSIM_CLKCTRL_REG 0x8 /* Clock control register */ | ||
34 | #define DSIM_TIMEOUT_REG 0xc /* Time out register */ | ||
35 | #define DSIM_CONFIG_REG 0x10 /* Configuration register */ | ||
36 | #define DSIM_ESCMODE_REG 0x14 /* Escape mode register */ | ||
37 | |||
38 | /* Main display image resolution register */ | ||
39 | #define DSIM_MDRESOL_REG 0x18 | ||
40 | #define DSIM_MVPORCH_REG 0x1c /* Main display Vporch register */ | ||
41 | #define DSIM_MHPORCH_REG 0x20 /* Main display Hporch register */ | ||
42 | #define DSIM_MSYNC_REG 0x24 /* Main display sync area register */ | ||
43 | |||
44 | /* Sub display image resolution register */ | ||
45 | #define DSIM_SDRESOL_REG 0x28 | ||
46 | #define DSIM_INTSRC_REG 0x2c /* Interrupt source register */ | ||
47 | #define DSIM_INTMSK_REG 0x30 /* Interrupt mask register */ | ||
48 | #define DSIM_PKTHDR_REG 0x34 /* Packet Header FIFO register */ | ||
49 | #define DSIM_PAYLOAD_REG 0x38 /* Payload FIFO register */ | ||
50 | #define DSIM_RXFIFO_REG 0x3c /* Read FIFO register */ | ||
51 | #define DSIM_FIFOTHLD_REG 0x40 /* FIFO threshold level register */ | ||
52 | #define DSIM_FIFOCTRL_REG 0x44 /* FIFO status and control register */ | ||
53 | |||
54 | /* FIFO memory AC characteristic register */ | ||
55 | #define DSIM_PLLCTRL_REG 0x4c /* PLL control register */ | ||
56 | #define DSIM_PLLTMR_REG 0x50 /* PLL timer register */ | ||
57 | #define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */ | ||
58 | #define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */ | ||
59 | |||
60 | /* DSIM_STATUS */ | ||
61 | #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) | ||
62 | #define DSIM_STOP_STATE_CLK (1 << 8) | ||
63 | #define DSIM_TX_READY_HS_CLK (1 << 10) | ||
64 | #define DSIM_PLL_STABLE (1 << 31) | ||
65 | |||
66 | /* DSIM_SWRST */ | ||
67 | #define DSIM_FUNCRST (1 << 16) | ||
68 | #define DSIM_SWRST (1 << 0) | ||
69 | |||
70 | /* DSIM_TIMEOUT */ | ||
71 | #define DSIM_LPDR_TIMEOUT(x) ((x) << 0) | ||
72 | #define DSIM_BTA_TIMEOUT(x) ((x) << 16) | ||
73 | |||
74 | /* DSIM_CLKCTRL */ | ||
75 | #define DSIM_ESC_PRESCALER(x) (((x) & 0xffff) << 0) | ||
76 | #define DSIM_ESC_PRESCALER_MASK (0xffff << 0) | ||
77 | #define DSIM_LANE_ESC_CLK_EN_CLK (1 << 19) | ||
78 | #define DSIM_LANE_ESC_CLK_EN_DATA(x) (((x) & 0xf) << 20) | ||
79 | #define DSIM_LANE_ESC_CLK_EN_DATA_MASK (0xf << 20) | ||
80 | #define DSIM_BYTE_CLKEN (1 << 24) | ||
81 | #define DSIM_BYTE_CLK_SRC(x) (((x) & 0x3) << 25) | ||
82 | #define DSIM_BYTE_CLK_SRC_MASK (0x3 << 25) | ||
83 | #define DSIM_PLL_BYPASS (1 << 27) | ||
84 | #define DSIM_ESC_CLKEN (1 << 28) | ||
85 | #define DSIM_TX_REQUEST_HSCLK (1 << 31) | ||
86 | |||
87 | /* DSIM_CONFIG */ | ||
88 | #define DSIM_LANE_EN_CLK (1 << 0) | ||
89 | #define DSIM_LANE_EN(x) (((x) & 0xf) << 1) | ||
90 | #define DSIM_NUM_OF_DATA_LANE(x) (((x) & 0x3) << 5) | ||
91 | #define DSIM_SUB_PIX_FORMAT(x) (((x) & 0x7) << 8) | ||
92 | #define DSIM_MAIN_PIX_FORMAT_MASK (0x7 << 12) | ||
93 | #define DSIM_MAIN_PIX_FORMAT_RGB888 (0x7 << 12) | ||
94 | #define DSIM_MAIN_PIX_FORMAT_RGB666 (0x6 << 12) | ||
95 | #define DSIM_MAIN_PIX_FORMAT_RGB666_P (0x5 << 12) | ||
96 | #define DSIM_MAIN_PIX_FORMAT_RGB565 (0x4 << 12) | ||
97 | #define DSIM_SUB_VC (((x) & 0x3) << 16) | ||
98 | #define DSIM_MAIN_VC (((x) & 0x3) << 18) | ||
99 | #define DSIM_HSA_MODE (1 << 20) | ||
100 | #define DSIM_HBP_MODE (1 << 21) | ||
101 | #define DSIM_HFP_MODE (1 << 22) | ||
102 | #define DSIM_HSE_MODE (1 << 23) | ||
103 | #define DSIM_AUTO_MODE (1 << 24) | ||
104 | #define DSIM_VIDEO_MODE (1 << 25) | ||
105 | #define DSIM_BURST_MODE (1 << 26) | ||
106 | #define DSIM_SYNC_INFORM (1 << 27) | ||
107 | #define DSIM_EOT_DISABLE (1 << 28) | ||
108 | #define DSIM_MFLUSH_VS (1 << 29) | ||
109 | |||
110 | /* DSIM_ESCMODE */ | ||
111 | #define DSIM_TX_TRIGGER_RST (1 << 4) | ||
112 | #define DSIM_TX_LPDT_LP (1 << 6) | ||
113 | #define DSIM_CMD_LPDT_LP (1 << 7) | ||
114 | #define DSIM_FORCE_BTA (1 << 16) | ||
115 | #define DSIM_FORCE_STOP_STATE (1 << 20) | ||
116 | #define DSIM_STOP_STATE_CNT(x) (((x) & 0x7ff) << 21) | ||
117 | #define DSIM_STOP_STATE_CNT_MASK (0x7ff << 21) | ||
118 | |||
119 | /* DSIM_MDRESOL */ | ||
120 | #define DSIM_MAIN_STAND_BY (1 << 31) | ||
121 | #define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) | ||
122 | #define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) | ||
123 | |||
124 | /* DSIM_MVPORCH */ | ||
125 | #define DSIM_CMD_ALLOW(x) ((x) << 28) | ||
126 | #define DSIM_STABLE_VFP(x) ((x) << 16) | ||
127 | #define DSIM_MAIN_VBP(x) ((x) << 0) | ||
128 | #define DSIM_CMD_ALLOW_MASK (0xf << 28) | ||
129 | #define DSIM_STABLE_VFP_MASK (0x7ff << 16) | ||
130 | #define DSIM_MAIN_VBP_MASK (0x7ff << 0) | ||
131 | |||
132 | /* DSIM_MHPORCH */ | ||
133 | #define DSIM_MAIN_HFP(x) ((x) << 16) | ||
134 | #define DSIM_MAIN_HBP(x) ((x) << 0) | ||
135 | #define DSIM_MAIN_HFP_MASK ((0xffff) << 16) | ||
136 | #define DSIM_MAIN_HBP_MASK ((0xffff) << 0) | ||
137 | |||
138 | /* DSIM_MSYNC */ | ||
139 | #define DSIM_MAIN_VSA(x) ((x) << 22) | ||
140 | #define DSIM_MAIN_HSA(x) ((x) << 0) | ||
141 | #define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) | ||
142 | #define DSIM_MAIN_HSA_MASK ((0xffff) << 0) | ||
143 | |||
144 | /* DSIM_SDRESOL */ | ||
145 | #define DSIM_SUB_STANDY(x) ((x) << 31) | ||
146 | #define DSIM_SUB_VRESOL(x) ((x) << 16) | ||
147 | #define DSIM_SUB_HRESOL(x) ((x) << 0) | ||
148 | #define DSIM_SUB_STANDY_MASK ((0x1) << 31) | ||
149 | #define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) | ||
150 | #define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) | ||
151 | |||
152 | /* DSIM_INTSRC */ | ||
153 | #define DSIM_INT_PLL_STABLE (1 << 31) | ||
154 | #define DSIM_INT_SW_RST_RELEASE (1 << 30) | ||
155 | #define DSIM_INT_SFR_FIFO_EMPTY (1 << 29) | ||
156 | #define DSIM_INT_BTA (1 << 25) | ||
157 | #define DSIM_INT_FRAME_DONE (1 << 24) | ||
158 | #define DSIM_INT_RX_TIMEOUT (1 << 21) | ||
159 | #define DSIM_INT_BTA_TIMEOUT (1 << 20) | ||
160 | #define DSIM_INT_RX_DONE (1 << 18) | ||
161 | #define DSIM_INT_RX_TE (1 << 17) | ||
162 | #define DSIM_INT_RX_ACK (1 << 16) | ||
163 | #define DSIM_INT_RX_ECC_ERR (1 << 15) | ||
164 | #define DSIM_INT_RX_CRC_ERR (1 << 14) | ||
165 | |||
166 | /* DSIM_FIFOCTRL */ | ||
167 | #define DSIM_RX_DATA_FULL (1 << 25) | ||
168 | #define DSIM_RX_DATA_EMPTY (1 << 24) | ||
169 | #define DSIM_SFR_HEADER_FULL (1 << 23) | ||
170 | #define DSIM_SFR_HEADER_EMPTY (1 << 22) | ||
171 | #define DSIM_SFR_PAYLOAD_FULL (1 << 21) | ||
172 | #define DSIM_SFR_PAYLOAD_EMPTY (1 << 20) | ||
173 | #define DSIM_I80_HEADER_FULL (1 << 19) | ||
174 | #define DSIM_I80_HEADER_EMPTY (1 << 18) | ||
175 | #define DSIM_I80_PAYLOAD_FULL (1 << 17) | ||
176 | #define DSIM_I80_PAYLOAD_EMPTY (1 << 16) | ||
177 | #define DSIM_SD_HEADER_FULL (1 << 15) | ||
178 | #define DSIM_SD_HEADER_EMPTY (1 << 14) | ||
179 | #define DSIM_SD_PAYLOAD_FULL (1 << 13) | ||
180 | #define DSIM_SD_PAYLOAD_EMPTY (1 << 12) | ||
181 | #define DSIM_MD_HEADER_FULL (1 << 11) | ||
182 | #define DSIM_MD_HEADER_EMPTY (1 << 10) | ||
183 | #define DSIM_MD_PAYLOAD_FULL (1 << 9) | ||
184 | #define DSIM_MD_PAYLOAD_EMPTY (1 << 8) | ||
185 | #define DSIM_RX_FIFO (1 << 4) | ||
186 | #define DSIM_SFR_FIFO (1 << 3) | ||
187 | #define DSIM_I80_FIFO (1 << 2) | ||
188 | #define DSIM_SD_FIFO (1 << 1) | ||
189 | #define DSIM_MD_FIFO (1 << 0) | ||
190 | |||
191 | /* DSIM_PHYACCHR */ | ||
192 | #define DSIM_AFC_EN (1 << 14) | ||
193 | #define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) | ||
194 | |||
195 | /* DSIM_PLLCTRL */ | ||
196 | #define DSIM_FREQ_BAND(x) ((x) << 24) | ||
197 | #define DSIM_PLL_EN (1 << 23) | ||
198 | #define DSIM_PLL_P(x) ((x) << 13) | ||
199 | #define DSIM_PLL_M(x) ((x) << 4) | ||
200 | #define DSIM_PLL_S(x) ((x) << 1) | ||
201 | |||
202 | #define DSI_MAX_BUS_WIDTH 4 | ||
203 | #define DSI_NUM_VIRTUAL_CHANNELS 4 | ||
204 | #define DSI_TX_FIFO_SIZE 2048 | ||
205 | #define DSI_RX_FIFO_SIZE 256 | ||
206 | #define DSI_XFER_TIMEOUT_MS 100 | ||
207 | #define DSI_RX_FIFO_EMPTY 0x30800002 | ||
208 | |||
209 | enum exynos_dsi_transfer_type { | ||
210 | EXYNOS_DSI_TX, | ||
211 | EXYNOS_DSI_RX, | ||
212 | }; | ||
213 | |||
214 | struct exynos_dsi_transfer { | ||
215 | struct list_head list; | ||
216 | struct completion completed; | ||
217 | int result; | ||
218 | u8 data_id; | ||
219 | u8 data[2]; | ||
220 | u16 flags; | ||
221 | |||
222 | const u8 *tx_payload; | ||
223 | u16 tx_len; | ||
224 | u16 tx_done; | ||
225 | |||
226 | u8 *rx_payload; | ||
227 | u16 rx_len; | ||
228 | u16 rx_done; | ||
229 | }; | ||
230 | |||
231 | #define DSIM_STATE_ENABLED BIT(0) | ||
232 | #define DSIM_STATE_INITIALIZED BIT(1) | ||
233 | #define DSIM_STATE_CMD_LPM BIT(2) | ||
234 | |||
235 | struct exynos_dsi { | ||
236 | struct mipi_dsi_host dsi_host; | ||
237 | struct drm_connector connector; | ||
238 | struct drm_encoder *encoder; | ||
239 | struct device_node *panel_node; | ||
240 | struct drm_panel *panel; | ||
241 | struct device *dev; | ||
242 | |||
243 | void __iomem *reg_base; | ||
244 | struct phy *phy; | ||
245 | struct clk *pll_clk; | ||
246 | struct clk *bus_clk; | ||
247 | struct regulator_bulk_data supplies[2]; | ||
248 | int irq; | ||
249 | |||
250 | u32 pll_clk_rate; | ||
251 | u32 burst_clk_rate; | ||
252 | u32 esc_clk_rate; | ||
253 | u32 lanes; | ||
254 | u32 mode_flags; | ||
255 | u32 format; | ||
256 | struct videomode vm; | ||
257 | |||
258 | int state; | ||
259 | struct drm_property *brightness; | ||
260 | struct completion completed; | ||
261 | |||
262 | spinlock_t transfer_lock; /* protects transfer_list */ | ||
263 | struct list_head transfer_list; | ||
264 | }; | ||
265 | |||
266 | #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) | ||
267 | #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector) | ||
268 | |||
269 | static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) | ||
270 | { | ||
271 | if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) | ||
272 | return; | ||
273 | |||
274 | dev_err(dsi->dev, "timeout waiting for reset\n"); | ||
275 | } | ||
276 | |||
277 | static void exynos_dsi_reset(struct exynos_dsi *dsi) | ||
278 | { | ||
279 | reinit_completion(&dsi->completed); | ||
280 | writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG); | ||
281 | } | ||
282 | |||
283 | #ifndef MHZ | ||
284 | #define MHZ (1000*1000) | ||
285 | #endif | ||
286 | |||
287 | static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, | ||
288 | unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s) | ||
289 | { | ||
290 | unsigned long best_freq = 0; | ||
291 | u32 min_delta = 0xffffffff; | ||
292 | u8 p_min, p_max; | ||
293 | u8 _p, uninitialized_var(best_p); | ||
294 | u16 _m, uninitialized_var(best_m); | ||
295 | u8 _s, uninitialized_var(best_s); | ||
296 | |||
297 | p_min = DIV_ROUND_UP(fin, (12 * MHZ)); | ||
298 | p_max = fin / (6 * MHZ); | ||
299 | |||
300 | for (_p = p_min; _p <= p_max; ++_p) { | ||
301 | for (_s = 0; _s <= 5; ++_s) { | ||
302 | u64 tmp; | ||
303 | u32 delta; | ||
304 | |||
305 | tmp = (u64)fout * (_p << _s); | ||
306 | do_div(tmp, fin); | ||
307 | _m = tmp; | ||
308 | if (_m < 41 || _m > 125) | ||
309 | continue; | ||
310 | |||
311 | tmp = (u64)_m * fin; | ||
312 | do_div(tmp, _p); | ||
313 | if (tmp < 500 * MHZ || tmp > 1000 * MHZ) | ||
314 | continue; | ||
315 | |||
316 | tmp = (u64)_m * fin; | ||
317 | do_div(tmp, _p << _s); | ||
318 | |||
319 | delta = abs(fout - tmp); | ||
320 | if (delta < min_delta) { | ||
321 | best_p = _p; | ||
322 | best_m = _m; | ||
323 | best_s = _s; | ||
324 | min_delta = delta; | ||
325 | best_freq = tmp; | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | if (best_freq) { | ||
331 | *p = best_p; | ||
332 | *m = best_m; | ||
333 | *s = best_s; | ||
334 | } | ||
335 | |||
336 | return best_freq; | ||
337 | } | ||
338 | |||
339 | static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, | ||
340 | unsigned long freq) | ||
341 | { | ||
342 | static const unsigned long freq_bands[] = { | ||
343 | 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ, | ||
344 | 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ, | ||
345 | 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ, | ||
346 | 770 * MHZ, 870 * MHZ, 950 * MHZ, | ||
347 | }; | ||
348 | unsigned long fin, fout; | ||
349 | int timeout, band; | ||
350 | u8 p, s; | ||
351 | u16 m; | ||
352 | u32 reg; | ||
353 | |||
354 | clk_set_rate(dsi->pll_clk, dsi->pll_clk_rate); | ||
355 | |||
356 | fin = clk_get_rate(dsi->pll_clk); | ||
357 | if (!fin) { | ||
358 | dev_err(dsi->dev, "failed to get PLL clock frequency\n"); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin); | ||
363 | |||
364 | fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s); | ||
365 | if (!fout) { | ||
366 | dev_err(dsi->dev, | ||
367 | "failed to find PLL PMS for requested frequency\n"); | ||
368 | return -EFAULT; | ||
369 | } | ||
370 | |||
371 | for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) | ||
372 | if (fout < freq_bands[band]) | ||
373 | break; | ||
374 | |||
375 | dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout, | ||
376 | p, m, s, band); | ||
377 | |||
378 | writel(500, dsi->reg_base + DSIM_PLLTMR_REG); | ||
379 | |||
380 | reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN | ||
381 | | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); | ||
382 | writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); | ||
383 | |||
384 | timeout = 1000; | ||
385 | do { | ||
386 | if (timeout-- == 0) { | ||
387 | dev_err(dsi->dev, "PLL failed to stabilize\n"); | ||
388 | return -EFAULT; | ||
389 | } | ||
390 | reg = readl(dsi->reg_base + DSIM_STATUS_REG); | ||
391 | } while ((reg & DSIM_PLL_STABLE) == 0); | ||
392 | |||
393 | return fout; | ||
394 | } | ||
395 | |||
396 | static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) | ||
397 | { | ||
398 | unsigned long hs_clk, byte_clk, esc_clk; | ||
399 | unsigned long esc_div; | ||
400 | u32 reg; | ||
401 | |||
402 | hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate); | ||
403 | if (!hs_clk) { | ||
404 | dev_err(dsi->dev, "failed to configure DSI PLL\n"); | ||
405 | return -EFAULT; | ||
406 | } | ||
407 | |||
408 | byte_clk = hs_clk / 8; | ||
409 | esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate); | ||
410 | esc_clk = byte_clk / esc_div; | ||
411 | |||
412 | if (esc_clk > 20 * MHZ) { | ||
413 | ++esc_div; | ||
414 | esc_clk = byte_clk / esc_div; | ||
415 | } | ||
416 | |||
417 | dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", | ||
418 | hs_clk, byte_clk, esc_clk); | ||
419 | |||
420 | reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); | ||
421 | reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK | ||
422 | | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS | ||
423 | | DSIM_BYTE_CLK_SRC_MASK); | ||
424 | reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN | ||
425 | | DSIM_ESC_PRESCALER(esc_div) | ||
426 | | DSIM_LANE_ESC_CLK_EN_CLK | ||
427 | | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) | ||
428 | | DSIM_BYTE_CLK_SRC(0) | ||
429 | | DSIM_TX_REQUEST_HSCLK; | ||
430 | writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) | ||
436 | { | ||
437 | u32 reg; | ||
438 | |||
439 | reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG); | ||
440 | reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | ||
441 | | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); | ||
442 | writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG); | ||
443 | |||
444 | reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG); | ||
445 | reg &= ~DSIM_PLL_EN; | ||
446 | writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG); | ||
447 | } | ||
448 | |||
449 | static int exynos_dsi_init_link(struct exynos_dsi *dsi) | ||
450 | { | ||
451 | int timeout; | ||
452 | u32 reg; | ||
453 | u32 lanes_mask; | ||
454 | |||
455 | /* Initialize FIFO pointers */ | ||
456 | reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); | ||
457 | reg &= ~0x1f; | ||
458 | writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); | ||
459 | |||
460 | usleep_range(9000, 11000); | ||
461 | |||
462 | reg |= 0x1f; | ||
463 | writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG); | ||
464 | |||
465 | usleep_range(9000, 11000); | ||
466 | |||
467 | /* DSI configuration */ | ||
468 | reg = 0; | ||
469 | |||
470 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | ||
471 | reg |= DSIM_VIDEO_MODE; | ||
472 | |||
473 | if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) | ||
474 | reg |= DSIM_MFLUSH_VS; | ||
475 | if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) | ||
476 | reg |= DSIM_EOT_DISABLE; | ||
477 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) | ||
478 | reg |= DSIM_SYNC_INFORM; | ||
479 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) | ||
480 | reg |= DSIM_BURST_MODE; | ||
481 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT) | ||
482 | reg |= DSIM_AUTO_MODE; | ||
483 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE) | ||
484 | reg |= DSIM_HSE_MODE; | ||
485 | if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)) | ||
486 | reg |= DSIM_HFP_MODE; | ||
487 | if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)) | ||
488 | reg |= DSIM_HBP_MODE; | ||
489 | if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA)) | ||
490 | reg |= DSIM_HSA_MODE; | ||
491 | } | ||
492 | |||
493 | switch (dsi->format) { | ||
494 | case MIPI_DSI_FMT_RGB888: | ||
495 | reg |= DSIM_MAIN_PIX_FORMAT_RGB888; | ||
496 | break; | ||
497 | case MIPI_DSI_FMT_RGB666: | ||
498 | reg |= DSIM_MAIN_PIX_FORMAT_RGB666; | ||
499 | break; | ||
500 | case MIPI_DSI_FMT_RGB666_PACKED: | ||
501 | reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P; | ||
502 | break; | ||
503 | case MIPI_DSI_FMT_RGB565: | ||
504 | reg |= DSIM_MAIN_PIX_FORMAT_RGB565; | ||
505 | break; | ||
506 | default: | ||
507 | dev_err(dsi->dev, "invalid pixel format\n"); | ||
508 | return -EINVAL; | ||
509 | } | ||
510 | |||
511 | reg |= DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1); | ||
512 | |||
513 | writel(reg, dsi->reg_base + DSIM_CONFIG_REG); | ||
514 | |||
515 | reg |= DSIM_LANE_EN_CLK; | ||
516 | writel(reg, dsi->reg_base + DSIM_CONFIG_REG); | ||
517 | |||
518 | lanes_mask = BIT(dsi->lanes) - 1; | ||
519 | reg |= DSIM_LANE_EN(lanes_mask); | ||
520 | writel(reg, dsi->reg_base + DSIM_CONFIG_REG); | ||
521 | |||
522 | /* Check clock and data lane state are stop state */ | ||
523 | timeout = 100; | ||
524 | do { | ||
525 | if (timeout-- == 0) { | ||
526 | dev_err(dsi->dev, "waiting for bus lanes timed out\n"); | ||
527 | return -EFAULT; | ||
528 | } | ||
529 | |||
530 | reg = readl(dsi->reg_base + DSIM_STATUS_REG); | ||
531 | if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) | ||
532 | != DSIM_STOP_STATE_DAT(lanes_mask)) | ||
533 | continue; | ||
534 | } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); | ||
535 | |||
536 | reg = readl(dsi->reg_base + DSIM_ESCMODE_REG); | ||
537 | reg &= ~DSIM_STOP_STATE_CNT_MASK; | ||
538 | reg |= DSIM_STOP_STATE_CNT(0xf); | ||
539 | writel(reg, dsi->reg_base + DSIM_ESCMODE_REG); | ||
540 | |||
541 | reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); | ||
542 | writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) | ||
548 | { | ||
549 | struct videomode *vm = &dsi->vm; | ||
550 | u32 reg; | ||
551 | |||
552 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | ||
553 | reg = DSIM_CMD_ALLOW(0xf) | ||
554 | | DSIM_STABLE_VFP(vm->vfront_porch) | ||
555 | | DSIM_MAIN_VBP(vm->vback_porch); | ||
556 | writel(reg, dsi->reg_base + DSIM_MVPORCH_REG); | ||
557 | |||
558 | reg = DSIM_MAIN_HFP(vm->hfront_porch) | ||
559 | | DSIM_MAIN_HBP(vm->hback_porch); | ||
560 | writel(reg, dsi->reg_base + DSIM_MHPORCH_REG); | ||
561 | |||
562 | reg = DSIM_MAIN_VSA(vm->vsync_len) | ||
563 | | DSIM_MAIN_HSA(vm->hsync_len); | ||
564 | writel(reg, dsi->reg_base + DSIM_MSYNC_REG); | ||
565 | } | ||
566 | |||
567 | reg = DSIM_MAIN_HRESOL(vm->hactive) | DSIM_MAIN_VRESOL(vm->vactive); | ||
568 | writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); | ||
569 | |||
570 | dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive); | ||
571 | } | ||
572 | |||
573 | static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) | ||
574 | { | ||
575 | u32 reg; | ||
576 | |||
577 | reg = readl(dsi->reg_base + DSIM_MDRESOL_REG); | ||
578 | if (enable) | ||
579 | reg |= DSIM_MAIN_STAND_BY; | ||
580 | else | ||
581 | reg &= ~DSIM_MAIN_STAND_BY; | ||
582 | writel(reg, dsi->reg_base + DSIM_MDRESOL_REG); | ||
583 | } | ||
584 | |||
585 | static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) | ||
586 | { | ||
587 | int timeout = 2000; | ||
588 | |||
589 | do { | ||
590 | u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG); | ||
591 | |||
592 | if (!(reg & DSIM_SFR_HEADER_FULL)) | ||
593 | return 0; | ||
594 | |||
595 | if (!cond_resched()) | ||
596 | usleep_range(950, 1050); | ||
597 | } while (--timeout); | ||
598 | |||
599 | return -ETIMEDOUT; | ||
600 | } | ||
601 | |||
602 | static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm) | ||
603 | { | ||
604 | u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); | ||
605 | |||
606 | if (lpm) | ||
607 | v |= DSIM_CMD_LPDT_LP; | ||
608 | else | ||
609 | v &= ~DSIM_CMD_LPDT_LP; | ||
610 | |||
611 | writel(v, dsi->reg_base + DSIM_ESCMODE_REG); | ||
612 | } | ||
613 | |||
614 | static void exynos_dsi_force_bta(struct exynos_dsi *dsi) | ||
615 | { | ||
616 | u32 v = readl(dsi->reg_base + DSIM_ESCMODE_REG); | ||
617 | |||
618 | v |= DSIM_FORCE_BTA; | ||
619 | writel(v, dsi->reg_base + DSIM_ESCMODE_REG); | ||
620 | } | ||
621 | |||
622 | static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, | ||
623 | struct exynos_dsi_transfer *xfer) | ||
624 | { | ||
625 | struct device *dev = dsi->dev; | ||
626 | const u8 *payload = xfer->tx_payload + xfer->tx_done; | ||
627 | u16 length = xfer->tx_len - xfer->tx_done; | ||
628 | bool first = !xfer->tx_done; | ||
629 | u32 reg; | ||
630 | |||
631 | dev_dbg(dev, "< xfer %p: tx len %u, done %u, rx len %u, done %u\n", | ||
632 | xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); | ||
633 | |||
634 | if (length > DSI_TX_FIFO_SIZE) | ||
635 | length = DSI_TX_FIFO_SIZE; | ||
636 | |||
637 | xfer->tx_done += length; | ||
638 | |||
639 | /* Send payload */ | ||
640 | while (length >= 4) { | ||
641 | reg = (payload[3] << 24) | (payload[2] << 16) | ||
642 | | (payload[1] << 8) | payload[0]; | ||
643 | writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); | ||
644 | payload += 4; | ||
645 | length -= 4; | ||
646 | } | ||
647 | |||
648 | reg = 0; | ||
649 | switch (length) { | ||
650 | case 3: | ||
651 | reg |= payload[2] << 16; | ||
652 | /* Fall through */ | ||
653 | case 2: | ||
654 | reg |= payload[1] << 8; | ||
655 | /* Fall through */ | ||
656 | case 1: | ||
657 | reg |= payload[0]; | ||
658 | writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG); | ||
659 | break; | ||
660 | case 0: | ||
661 | /* Do nothing */ | ||
662 | break; | ||
663 | } | ||
664 | |||
665 | /* Send packet header */ | ||
666 | if (!first) | ||
667 | return; | ||
668 | |||
669 | reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->data_id; | ||
670 | if (exynos_dsi_wait_for_hdr_fifo(dsi)) { | ||
671 | dev_err(dev, "waiting for header FIFO timed out\n"); | ||
672 | return; | ||
673 | } | ||
674 | |||
675 | if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM, | ||
676 | dsi->state & DSIM_STATE_CMD_LPM)) { | ||
677 | exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM); | ||
678 | dsi->state ^= DSIM_STATE_CMD_LPM; | ||
679 | } | ||
680 | |||
681 | writel(reg, dsi->reg_base + DSIM_PKTHDR_REG); | ||
682 | |||
683 | if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) | ||
684 | exynos_dsi_force_bta(dsi); | ||
685 | } | ||
686 | |||
687 | static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, | ||
688 | struct exynos_dsi_transfer *xfer) | ||
689 | { | ||
690 | u8 *payload = xfer->rx_payload + xfer->rx_done; | ||
691 | bool first = !xfer->rx_done; | ||
692 | struct device *dev = dsi->dev; | ||
693 | u16 length; | ||
694 | u32 reg; | ||
695 | |||
696 | if (first) { | ||
697 | reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); | ||
698 | |||
699 | switch (reg & 0x3f) { | ||
700 | case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: | ||
701 | case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: | ||
702 | if (xfer->rx_len >= 2) { | ||
703 | payload[1] = reg >> 16; | ||
704 | ++xfer->rx_done; | ||
705 | } | ||
706 | /* Fall through */ | ||
707 | case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: | ||
708 | case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: | ||
709 | payload[0] = reg >> 8; | ||
710 | ++xfer->rx_done; | ||
711 | xfer->rx_len = xfer->rx_done; | ||
712 | xfer->result = 0; | ||
713 | goto clear_fifo; | ||
714 | case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: | ||
715 | dev_err(dev, "DSI Error Report: 0x%04x\n", | ||
716 | (reg >> 8) & 0xffff); | ||
717 | xfer->result = 0; | ||
718 | goto clear_fifo; | ||
719 | } | ||
720 | |||
721 | length = (reg >> 8) & 0xffff; | ||
722 | if (length > xfer->rx_len) { | ||
723 | dev_err(dev, | ||
724 | "response too long (%u > %u bytes), stripping\n", | ||
725 | xfer->rx_len, length); | ||
726 | length = xfer->rx_len; | ||
727 | } else if (length < xfer->rx_len) | ||
728 | xfer->rx_len = length; | ||
729 | } | ||
730 | |||
731 | length = xfer->rx_len - xfer->rx_done; | ||
732 | xfer->rx_done += length; | ||
733 | |||
734 | /* Receive payload */ | ||
735 | while (length >= 4) { | ||
736 | reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); | ||
737 | payload[0] = (reg >> 0) & 0xff; | ||
738 | payload[1] = (reg >> 8) & 0xff; | ||
739 | payload[2] = (reg >> 16) & 0xff; | ||
740 | payload[3] = (reg >> 24) & 0xff; | ||
741 | payload += 4; | ||
742 | length -= 4; | ||
743 | } | ||
744 | |||
745 | if (length) { | ||
746 | reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); | ||
747 | switch (length) { | ||
748 | case 3: | ||
749 | payload[2] = (reg >> 16) & 0xff; | ||
750 | /* Fall through */ | ||
751 | case 2: | ||
752 | payload[1] = (reg >> 8) & 0xff; | ||
753 | /* Fall through */ | ||
754 | case 1: | ||
755 | payload[0] = reg & 0xff; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | if (xfer->rx_done == xfer->rx_len) | ||
760 | xfer->result = 0; | ||
761 | |||
762 | clear_fifo: | ||
763 | length = DSI_RX_FIFO_SIZE / 4; | ||
764 | do { | ||
765 | reg = readl(dsi->reg_base + DSIM_RXFIFO_REG); | ||
766 | if (reg == DSI_RX_FIFO_EMPTY) | ||
767 | break; | ||
768 | } while (--length); | ||
769 | } | ||
770 | |||
771 | static void exynos_dsi_transfer_start(struct exynos_dsi *dsi) | ||
772 | { | ||
773 | unsigned long flags; | ||
774 | struct exynos_dsi_transfer *xfer; | ||
775 | bool start = false; | ||
776 | |||
777 | again: | ||
778 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
779 | |||
780 | if (list_empty(&dsi->transfer_list)) { | ||
781 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
782 | return; | ||
783 | } | ||
784 | |||
785 | xfer = list_first_entry(&dsi->transfer_list, | ||
786 | struct exynos_dsi_transfer, list); | ||
787 | |||
788 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
789 | |||
790 | if (xfer->tx_len && xfer->tx_done == xfer->tx_len) | ||
791 | /* waiting for RX */ | ||
792 | return; | ||
793 | |||
794 | exynos_dsi_send_to_fifo(dsi, xfer); | ||
795 | |||
796 | if (xfer->tx_len || xfer->rx_len) | ||
797 | return; | ||
798 | |||
799 | xfer->result = 0; | ||
800 | complete(&xfer->completed); | ||
801 | |||
802 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
803 | |||
804 | list_del_init(&xfer->list); | ||
805 | start = !list_empty(&dsi->transfer_list); | ||
806 | |||
807 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
808 | |||
809 | if (start) | ||
810 | goto again; | ||
811 | } | ||
812 | |||
813 | static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi) | ||
814 | { | ||
815 | struct exynos_dsi_transfer *xfer; | ||
816 | unsigned long flags; | ||
817 | bool start = true; | ||
818 | |||
819 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
820 | |||
821 | if (list_empty(&dsi->transfer_list)) { | ||
822 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
823 | return false; | ||
824 | } | ||
825 | |||
826 | xfer = list_first_entry(&dsi->transfer_list, | ||
827 | struct exynos_dsi_transfer, list); | ||
828 | |||
829 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
830 | |||
831 | dev_dbg(dsi->dev, | ||
832 | "> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n", | ||
833 | xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); | ||
834 | |||
835 | if (xfer->tx_done != xfer->tx_len) | ||
836 | return true; | ||
837 | |||
838 | if (xfer->rx_done != xfer->rx_len) | ||
839 | exynos_dsi_read_from_fifo(dsi, xfer); | ||
840 | |||
841 | if (xfer->rx_done != xfer->rx_len) | ||
842 | return true; | ||
843 | |||
844 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
845 | |||
846 | list_del_init(&xfer->list); | ||
847 | start = !list_empty(&dsi->transfer_list); | ||
848 | |||
849 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
850 | |||
851 | if (!xfer->rx_len) | ||
852 | xfer->result = 0; | ||
853 | complete(&xfer->completed); | ||
854 | |||
855 | return start; | ||
856 | } | ||
857 | |||
858 | static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi, | ||
859 | struct exynos_dsi_transfer *xfer) | ||
860 | { | ||
861 | unsigned long flags; | ||
862 | bool start; | ||
863 | |||
864 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
865 | |||
866 | if (!list_empty(&dsi->transfer_list) && | ||
867 | xfer == list_first_entry(&dsi->transfer_list, | ||
868 | struct exynos_dsi_transfer, list)) { | ||
869 | list_del_init(&xfer->list); | ||
870 | start = !list_empty(&dsi->transfer_list); | ||
871 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
872 | if (start) | ||
873 | exynos_dsi_transfer_start(dsi); | ||
874 | return; | ||
875 | } | ||
876 | |||
877 | list_del_init(&xfer->list); | ||
878 | |||
879 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
880 | } | ||
881 | |||
882 | static int exynos_dsi_transfer(struct exynos_dsi *dsi, | ||
883 | struct exynos_dsi_transfer *xfer) | ||
884 | { | ||
885 | unsigned long flags; | ||
886 | bool stopped; | ||
887 | |||
888 | xfer->tx_done = 0; | ||
889 | xfer->rx_done = 0; | ||
890 | xfer->result = -ETIMEDOUT; | ||
891 | init_completion(&xfer->completed); | ||
892 | |||
893 | spin_lock_irqsave(&dsi->transfer_lock, flags); | ||
894 | |||
895 | stopped = list_empty(&dsi->transfer_list); | ||
896 | list_add_tail(&xfer->list, &dsi->transfer_list); | ||
897 | |||
898 | spin_unlock_irqrestore(&dsi->transfer_lock, flags); | ||
899 | |||
900 | if (stopped) | ||
901 | exynos_dsi_transfer_start(dsi); | ||
902 | |||
903 | wait_for_completion_timeout(&xfer->completed, | ||
904 | msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); | ||
905 | if (xfer->result == -ETIMEDOUT) { | ||
906 | exynos_dsi_remove_transfer(dsi, xfer); | ||
907 | dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 2, xfer->data, | ||
908 | xfer->tx_len, xfer->tx_payload); | ||
909 | return -ETIMEDOUT; | ||
910 | } | ||
911 | |||
912 | /* Also covers hardware timeout condition */ | ||
913 | return xfer->result; | ||
914 | } | ||
915 | |||
916 | static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) | ||
917 | { | ||
918 | struct exynos_dsi *dsi = dev_id; | ||
919 | u32 status; | ||
920 | |||
921 | status = readl(dsi->reg_base + DSIM_INTSRC_REG); | ||
922 | if (!status) { | ||
923 | static unsigned long int j; | ||
924 | if (printk_timed_ratelimit(&j, 500)) | ||
925 | dev_warn(dsi->dev, "spurious interrupt\n"); | ||
926 | return IRQ_HANDLED; | ||
927 | } | ||
928 | writel(status, dsi->reg_base + DSIM_INTSRC_REG); | ||
929 | |||
930 | if (status & DSIM_INT_SW_RST_RELEASE) { | ||
931 | u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY); | ||
932 | writel(mask, dsi->reg_base + DSIM_INTMSK_REG); | ||
933 | complete(&dsi->completed); | ||
934 | return IRQ_HANDLED; | ||
935 | } | ||
936 | |||
937 | if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY))) | ||
938 | return IRQ_HANDLED; | ||
939 | |||
940 | if (exynos_dsi_transfer_finish(dsi)) | ||
941 | exynos_dsi_transfer_start(dsi); | ||
942 | |||
943 | return IRQ_HANDLED; | ||
944 | } | ||
945 | |||
946 | static int exynos_dsi_init(struct exynos_dsi *dsi) | ||
947 | { | ||
948 | exynos_dsi_enable_clock(dsi); | ||
949 | exynos_dsi_reset(dsi); | ||
950 | enable_irq(dsi->irq); | ||
951 | exynos_dsi_wait_for_reset(dsi); | ||
952 | exynos_dsi_init_link(dsi); | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | static int exynos_dsi_host_attach(struct mipi_dsi_host *host, | ||
958 | struct mipi_dsi_device *device) | ||
959 | { | ||
960 | struct exynos_dsi *dsi = host_to_dsi(host); | ||
961 | |||
962 | dsi->lanes = device->lanes; | ||
963 | dsi->format = device->format; | ||
964 | dsi->mode_flags = device->mode_flags; | ||
965 | dsi->panel_node = device->dev.of_node; | ||
966 | |||
967 | if (dsi->connector.dev) | ||
968 | drm_helper_hpd_irq_event(dsi->connector.dev); | ||
969 | |||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | static int exynos_dsi_host_detach(struct mipi_dsi_host *host, | ||
974 | struct mipi_dsi_device *device) | ||
975 | { | ||
976 | struct exynos_dsi *dsi = host_to_dsi(host); | ||
977 | |||
978 | dsi->panel_node = NULL; | ||
979 | |||
980 | if (dsi->connector.dev) | ||
981 | drm_helper_hpd_irq_event(dsi->connector.dev); | ||
982 | |||
983 | return 0; | ||
984 | } | ||
985 | |||
986 | /* distinguish between short and long DSI packet types */ | ||
987 | static bool exynos_dsi_is_short_dsi_type(u8 type) | ||
988 | { | ||
989 | return (type & 0x0f) <= 8; | ||
990 | } | ||
991 | |||
992 | static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, | ||
993 | struct mipi_dsi_msg *msg) | ||
994 | { | ||
995 | struct exynos_dsi *dsi = host_to_dsi(host); | ||
996 | struct exynos_dsi_transfer xfer; | ||
997 | int ret; | ||
998 | |||
999 | if (!(dsi->state & DSIM_STATE_INITIALIZED)) { | ||
1000 | ret = exynos_dsi_init(dsi); | ||
1001 | if (ret) | ||
1002 | return ret; | ||
1003 | dsi->state |= DSIM_STATE_INITIALIZED; | ||
1004 | } | ||
1005 | |||
1006 | if (msg->tx_len == 0) | ||
1007 | return -EINVAL; | ||
1008 | |||
1009 | xfer.data_id = msg->type | (msg->channel << 6); | ||
1010 | |||
1011 | if (exynos_dsi_is_short_dsi_type(msg->type)) { | ||
1012 | const char *tx_buf = msg->tx_buf; | ||
1013 | |||
1014 | if (msg->tx_len > 2) | ||
1015 | return -EINVAL; | ||
1016 | xfer.tx_len = 0; | ||
1017 | xfer.data[0] = tx_buf[0]; | ||
1018 | xfer.data[1] = (msg->tx_len == 2) ? tx_buf[1] : 0; | ||
1019 | } else { | ||
1020 | xfer.tx_len = msg->tx_len; | ||
1021 | xfer.data[0] = msg->tx_len & 0xff; | ||
1022 | xfer.data[1] = msg->tx_len >> 8; | ||
1023 | xfer.tx_payload = msg->tx_buf; | ||
1024 | } | ||
1025 | |||
1026 | xfer.rx_len = msg->rx_len; | ||
1027 | xfer.rx_payload = msg->rx_buf; | ||
1028 | xfer.flags = msg->flags; | ||
1029 | |||
1030 | ret = exynos_dsi_transfer(dsi, &xfer); | ||
1031 | return (ret < 0) ? ret : xfer.rx_done; | ||
1032 | } | ||
1033 | |||
1034 | static const struct mipi_dsi_host_ops exynos_dsi_ops = { | ||
1035 | .attach = exynos_dsi_host_attach, | ||
1036 | .detach = exynos_dsi_host_detach, | ||
1037 | .transfer = exynos_dsi_host_transfer, | ||
1038 | }; | ||
1039 | |||
1040 | static int exynos_dsi_poweron(struct exynos_dsi *dsi) | ||
1041 | { | ||
1042 | int ret; | ||
1043 | |||
1044 | ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | ||
1045 | if (ret < 0) { | ||
1046 | dev_err(dsi->dev, "cannot enable regulators %d\n", ret); | ||
1047 | return ret; | ||
1048 | } | ||
1049 | |||
1050 | ret = clk_prepare_enable(dsi->bus_clk); | ||
1051 | if (ret < 0) { | ||
1052 | dev_err(dsi->dev, "cannot enable bus clock %d\n", ret); | ||
1053 | goto err_bus_clk; | ||
1054 | } | ||
1055 | |||
1056 | ret = clk_prepare_enable(dsi->pll_clk); | ||
1057 | if (ret < 0) { | ||
1058 | dev_err(dsi->dev, "cannot enable pll clock %d\n", ret); | ||
1059 | goto err_pll_clk; | ||
1060 | } | ||
1061 | |||
1062 | ret = phy_power_on(dsi->phy); | ||
1063 | if (ret < 0) { | ||
1064 | dev_err(dsi->dev, "cannot enable phy %d\n", ret); | ||
1065 | goto err_phy; | ||
1066 | } | ||
1067 | |||
1068 | return 0; | ||
1069 | |||
1070 | err_phy: | ||
1071 | clk_disable_unprepare(dsi->pll_clk); | ||
1072 | err_pll_clk: | ||
1073 | clk_disable_unprepare(dsi->bus_clk); | ||
1074 | err_bus_clk: | ||
1075 | regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | ||
1076 | |||
1077 | return ret; | ||
1078 | } | ||
1079 | |||
1080 | static void exynos_dsi_poweroff(struct exynos_dsi *dsi) | ||
1081 | { | ||
1082 | int ret; | ||
1083 | |||
1084 | usleep_range(10000, 20000); | ||
1085 | |||
1086 | if (dsi->state & DSIM_STATE_INITIALIZED) { | ||
1087 | dsi->state &= ~DSIM_STATE_INITIALIZED; | ||
1088 | |||
1089 | exynos_dsi_disable_clock(dsi); | ||
1090 | |||
1091 | disable_irq(dsi->irq); | ||
1092 | } | ||
1093 | |||
1094 | dsi->state &= ~DSIM_STATE_CMD_LPM; | ||
1095 | |||
1096 | phy_power_off(dsi->phy); | ||
1097 | |||
1098 | clk_disable_unprepare(dsi->pll_clk); | ||
1099 | clk_disable_unprepare(dsi->bus_clk); | ||
1100 | |||
1101 | ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies); | ||
1102 | if (ret < 0) | ||
1103 | dev_err(dsi->dev, "cannot disable regulators %d\n", ret); | ||
1104 | } | ||
1105 | |||
1106 | static int exynos_dsi_enable(struct exynos_dsi *dsi) | ||
1107 | { | ||
1108 | int ret; | ||
1109 | |||
1110 | if (dsi->state & DSIM_STATE_ENABLED) | ||
1111 | return 0; | ||
1112 | |||
1113 | ret = exynos_dsi_poweron(dsi); | ||
1114 | if (ret < 0) | ||
1115 | return ret; | ||
1116 | |||
1117 | ret = drm_panel_enable(dsi->panel); | ||
1118 | if (ret < 0) { | ||
1119 | exynos_dsi_poweroff(dsi); | ||
1120 | return ret; | ||
1121 | } | ||
1122 | |||
1123 | exynos_dsi_set_display_mode(dsi); | ||
1124 | exynos_dsi_set_display_enable(dsi, true); | ||
1125 | |||
1126 | dsi->state |= DSIM_STATE_ENABLED; | ||
1127 | |||
1128 | return 0; | ||
1129 | } | ||
1130 | |||
1131 | static void exynos_dsi_disable(struct exynos_dsi *dsi) | ||
1132 | { | ||
1133 | if (!(dsi->state & DSIM_STATE_ENABLED)) | ||
1134 | return; | ||
1135 | |||
1136 | exynos_dsi_set_display_enable(dsi, false); | ||
1137 | drm_panel_disable(dsi->panel); | ||
1138 | exynos_dsi_poweroff(dsi); | ||
1139 | |||
1140 | dsi->state &= ~DSIM_STATE_ENABLED; | ||
1141 | } | ||
1142 | |||
1143 | static void exynos_dsi_dpms(struct exynos_drm_display *display, int mode) | ||
1144 | { | ||
1145 | struct exynos_dsi *dsi = display->ctx; | ||
1146 | |||
1147 | if (dsi->panel) { | ||
1148 | switch (mode) { | ||
1149 | case DRM_MODE_DPMS_ON: | ||
1150 | exynos_dsi_enable(dsi); | ||
1151 | break; | ||
1152 | case DRM_MODE_DPMS_STANDBY: | ||
1153 | case DRM_MODE_DPMS_SUSPEND: | ||
1154 | case DRM_MODE_DPMS_OFF: | ||
1155 | exynos_dsi_disable(dsi); | ||
1156 | break; | ||
1157 | default: | ||
1158 | break; | ||
1159 | } | ||
1160 | } | ||
1161 | } | ||
1162 | |||
1163 | static enum drm_connector_status | ||
1164 | exynos_dsi_detect(struct drm_connector *connector, bool force) | ||
1165 | { | ||
1166 | struct exynos_dsi *dsi = connector_to_dsi(connector); | ||
1167 | |||
1168 | if (!dsi->panel) { | ||
1169 | dsi->panel = of_drm_find_panel(dsi->panel_node); | ||
1170 | if (dsi->panel) | ||
1171 | drm_panel_attach(dsi->panel, &dsi->connector); | ||
1172 | } else if (!dsi->panel_node) { | ||
1173 | struct exynos_drm_display *display; | ||
1174 | |||
1175 | display = platform_get_drvdata(to_platform_device(dsi->dev)); | ||
1176 | exynos_dsi_dpms(display, DRM_MODE_DPMS_OFF); | ||
1177 | drm_panel_detach(dsi->panel); | ||
1178 | dsi->panel = NULL; | ||
1179 | } | ||
1180 | |||
1181 | if (dsi->panel) | ||
1182 | return connector_status_connected; | ||
1183 | |||
1184 | return connector_status_disconnected; | ||
1185 | } | ||
1186 | |||
1187 | static void exynos_dsi_connector_destroy(struct drm_connector *connector) | ||
1188 | { | ||
1189 | } | ||
1190 | |||
1191 | static struct drm_connector_funcs exynos_dsi_connector_funcs = { | ||
1192 | .dpms = drm_helper_connector_dpms, | ||
1193 | .detect = exynos_dsi_detect, | ||
1194 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
1195 | .destroy = exynos_dsi_connector_destroy, | ||
1196 | }; | ||
1197 | |||
1198 | static int exynos_dsi_get_modes(struct drm_connector *connector) | ||
1199 | { | ||
1200 | struct exynos_dsi *dsi = connector_to_dsi(connector); | ||
1201 | |||
1202 | if (dsi->panel) | ||
1203 | return dsi->panel->funcs->get_modes(dsi->panel); | ||
1204 | |||
1205 | return 0; | ||
1206 | } | ||
1207 | |||
1208 | static int exynos_dsi_mode_valid(struct drm_connector *connector, | ||
1209 | struct drm_display_mode *mode) | ||
1210 | { | ||
1211 | return MODE_OK; | ||
1212 | } | ||
1213 | |||
1214 | static struct drm_encoder * | ||
1215 | exynos_dsi_best_encoder(struct drm_connector *connector) | ||
1216 | { | ||
1217 | struct exynos_dsi *dsi = connector_to_dsi(connector); | ||
1218 | |||
1219 | return dsi->encoder; | ||
1220 | } | ||
1221 | |||
1222 | static struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = { | ||
1223 | .get_modes = exynos_dsi_get_modes, | ||
1224 | .mode_valid = exynos_dsi_mode_valid, | ||
1225 | .best_encoder = exynos_dsi_best_encoder, | ||
1226 | }; | ||
1227 | |||
1228 | static int exynos_dsi_create_connector(struct exynos_drm_display *display, | ||
1229 | struct drm_encoder *encoder) | ||
1230 | { | ||
1231 | struct exynos_dsi *dsi = display->ctx; | ||
1232 | struct drm_connector *connector = &dsi->connector; | ||
1233 | int ret; | ||
1234 | |||
1235 | dsi->encoder = encoder; | ||
1236 | |||
1237 | connector->polled = DRM_CONNECTOR_POLL_HPD; | ||
1238 | |||
1239 | ret = drm_connector_init(encoder->dev, connector, | ||
1240 | &exynos_dsi_connector_funcs, | ||
1241 | DRM_MODE_CONNECTOR_DSI); | ||
1242 | if (ret) { | ||
1243 | DRM_ERROR("Failed to initialize connector with drm\n"); | ||
1244 | return ret; | ||
1245 | } | ||
1246 | |||
1247 | drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); | ||
1248 | drm_sysfs_connector_add(connector); | ||
1249 | drm_mode_connector_attach_encoder(connector, encoder); | ||
1250 | |||
1251 | return 0; | ||
1252 | } | ||
1253 | |||
1254 | static void exynos_dsi_mode_set(struct exynos_drm_display *display, | ||
1255 | struct drm_display_mode *mode) | ||
1256 | { | ||
1257 | struct exynos_dsi *dsi = display->ctx; | ||
1258 | struct videomode *vm = &dsi->vm; | ||
1259 | |||
1260 | vm->hactive = mode->hdisplay; | ||
1261 | vm->vactive = mode->vdisplay; | ||
1262 | vm->vfront_porch = mode->vsync_start - mode->vdisplay; | ||
1263 | vm->vback_porch = mode->vtotal - mode->vsync_end; | ||
1264 | vm->vsync_len = mode->vsync_end - mode->vsync_start; | ||
1265 | vm->hfront_porch = mode->hsync_start - mode->hdisplay; | ||
1266 | vm->hback_porch = mode->htotal - mode->hsync_end; | ||
1267 | vm->hsync_len = mode->hsync_end - mode->hsync_start; | ||
1268 | } | ||
1269 | |||
1270 | static struct exynos_drm_display_ops exynos_dsi_display_ops = { | ||
1271 | .create_connector = exynos_dsi_create_connector, | ||
1272 | .mode_set = exynos_dsi_mode_set, | ||
1273 | .dpms = exynos_dsi_dpms | ||
1274 | }; | ||
1275 | |||
1276 | static struct exynos_drm_display exynos_dsi_display = { | ||
1277 | .type = EXYNOS_DISPLAY_TYPE_LCD, | ||
1278 | .ops = &exynos_dsi_display_ops, | ||
1279 | }; | ||
1280 | |||
1281 | /* of_* functions will be removed after merge of of_graph patches */ | ||
1282 | static struct device_node * | ||
1283 | of_get_child_by_name_reg(struct device_node *parent, const char *name, u32 reg) | ||
1284 | { | ||
1285 | struct device_node *np; | ||
1286 | |||
1287 | for_each_child_of_node(parent, np) { | ||
1288 | u32 r; | ||
1289 | |||
1290 | if (!np->name || of_node_cmp(np->name, name)) | ||
1291 | continue; | ||
1292 | |||
1293 | if (of_property_read_u32(np, "reg", &r) < 0) | ||
1294 | r = 0; | ||
1295 | |||
1296 | if (reg == r) | ||
1297 | break; | ||
1298 | } | ||
1299 | |||
1300 | return np; | ||
1301 | } | ||
1302 | |||
1303 | static struct device_node *of_graph_get_port_by_reg(struct device_node *parent, | ||
1304 | u32 reg) | ||
1305 | { | ||
1306 | struct device_node *ports, *port; | ||
1307 | |||
1308 | ports = of_get_child_by_name(parent, "ports"); | ||
1309 | if (ports) | ||
1310 | parent = ports; | ||
1311 | |||
1312 | port = of_get_child_by_name_reg(parent, "port", reg); | ||
1313 | |||
1314 | of_node_put(ports); | ||
1315 | |||
1316 | return port; | ||
1317 | } | ||
1318 | |||
1319 | static struct device_node * | ||
1320 | of_graph_get_endpoint_by_reg(struct device_node *port, u32 reg) | ||
1321 | { | ||
1322 | return of_get_child_by_name_reg(port, "endpoint", reg); | ||
1323 | } | ||
1324 | |||
1325 | static int exynos_dsi_of_read_u32(const struct device_node *np, | ||
1326 | const char *propname, u32 *out_value) | ||
1327 | { | ||
1328 | int ret = of_property_read_u32(np, propname, out_value); | ||
1329 | |||
1330 | if (ret < 0) | ||
1331 | pr_err("%s: failed to get '%s' property\n", np->full_name, | ||
1332 | propname); | ||
1333 | |||
1334 | return ret; | ||
1335 | } | ||
1336 | |||
1337 | enum { | ||
1338 | DSI_PORT_IN, | ||
1339 | DSI_PORT_OUT | ||
1340 | }; | ||
1341 | |||
1342 | static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) | ||
1343 | { | ||
1344 | struct device *dev = dsi->dev; | ||
1345 | struct device_node *node = dev->of_node; | ||
1346 | struct device_node *port, *ep; | ||
1347 | int ret; | ||
1348 | |||
1349 | ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency", | ||
1350 | &dsi->pll_clk_rate); | ||
1351 | if (ret < 0) | ||
1352 | return ret; | ||
1353 | |||
1354 | port = of_graph_get_port_by_reg(node, DSI_PORT_OUT); | ||
1355 | if (!port) { | ||
1356 | dev_err(dev, "no output port specified\n"); | ||
1357 | return -EINVAL; | ||
1358 | } | ||
1359 | |||
1360 | ep = of_graph_get_endpoint_by_reg(port, 0); | ||
1361 | of_node_put(port); | ||
1362 | if (!ep) { | ||
1363 | dev_err(dev, "no endpoint specified in output port\n"); | ||
1364 | return -EINVAL; | ||
1365 | } | ||
1366 | |||
1367 | ret = exynos_dsi_of_read_u32(ep, "samsung,burst-clock-frequency", | ||
1368 | &dsi->burst_clk_rate); | ||
1369 | if (ret < 0) | ||
1370 | goto end; | ||
1371 | |||
1372 | ret = exynos_dsi_of_read_u32(ep, "samsung,esc-clock-frequency", | ||
1373 | &dsi->esc_clk_rate); | ||
1374 | |||
1375 | end: | ||
1376 | of_node_put(ep); | ||
1377 | |||
1378 | return ret; | ||
1379 | } | ||
1380 | |||
1381 | static int exynos_dsi_probe(struct platform_device *pdev) | ||
1382 | { | ||
1383 | struct resource *res; | ||
1384 | struct exynos_dsi *dsi; | ||
1385 | int ret; | ||
1386 | |||
1387 | dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); | ||
1388 | if (!dsi) { | ||
1389 | dev_err(&pdev->dev, "failed to allocate dsi object.\n"); | ||
1390 | return -ENOMEM; | ||
1391 | } | ||
1392 | |||
1393 | init_completion(&dsi->completed); | ||
1394 | spin_lock_init(&dsi->transfer_lock); | ||
1395 | INIT_LIST_HEAD(&dsi->transfer_list); | ||
1396 | |||
1397 | dsi->dsi_host.ops = &exynos_dsi_ops; | ||
1398 | dsi->dsi_host.dev = &pdev->dev; | ||
1399 | |||
1400 | dsi->dev = &pdev->dev; | ||
1401 | |||
1402 | ret = exynos_dsi_parse_dt(dsi); | ||
1403 | if (ret) | ||
1404 | return ret; | ||
1405 | |||
1406 | dsi->supplies[0].supply = "vddcore"; | ||
1407 | dsi->supplies[1].supply = "vddio"; | ||
1408 | ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(dsi->supplies), | ||
1409 | dsi->supplies); | ||
1410 | if (ret) { | ||
1411 | dev_info(&pdev->dev, "failed to get regulators: %d\n", ret); | ||
1412 | return -EPROBE_DEFER; | ||
1413 | } | ||
1414 | |||
1415 | dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk"); | ||
1416 | if (IS_ERR(dsi->pll_clk)) { | ||
1417 | dev_info(&pdev->dev, "failed to get dsi pll input clock\n"); | ||
1418 | return -EPROBE_DEFER; | ||
1419 | } | ||
1420 | |||
1421 | dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk"); | ||
1422 | if (IS_ERR(dsi->bus_clk)) { | ||
1423 | dev_info(&pdev->dev, "failed to get dsi bus clock\n"); | ||
1424 | return -EPROBE_DEFER; | ||
1425 | } | ||
1426 | |||
1427 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1428 | dsi->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
1429 | if (!dsi->reg_base) { | ||
1430 | dev_err(&pdev->dev, "failed to remap io region\n"); | ||
1431 | return -EADDRNOTAVAIL; | ||
1432 | } | ||
1433 | |||
1434 | dsi->phy = devm_phy_get(&pdev->dev, "dsim"); | ||
1435 | if (IS_ERR(dsi->phy)) { | ||
1436 | dev_info(&pdev->dev, "failed to get dsim phy\n"); | ||
1437 | return -EPROBE_DEFER; | ||
1438 | } | ||
1439 | |||
1440 | dsi->irq = platform_get_irq(pdev, 0); | ||
1441 | if (dsi->irq < 0) { | ||
1442 | dev_err(&pdev->dev, "failed to request dsi irq resource\n"); | ||
1443 | return dsi->irq; | ||
1444 | } | ||
1445 | |||
1446 | irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN); | ||
1447 | ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL, | ||
1448 | exynos_dsi_irq, IRQF_ONESHOT, | ||
1449 | dev_name(&pdev->dev), dsi); | ||
1450 | if (ret) { | ||
1451 | dev_err(&pdev->dev, "failed to request dsi irq\n"); | ||
1452 | return ret; | ||
1453 | } | ||
1454 | |||
1455 | exynos_dsi_display.ctx = dsi; | ||
1456 | |||
1457 | platform_set_drvdata(pdev, &exynos_dsi_display); | ||
1458 | exynos_drm_display_register(&exynos_dsi_display); | ||
1459 | |||
1460 | return mipi_dsi_host_register(&dsi->dsi_host); | ||
1461 | } | ||
1462 | |||
1463 | static int exynos_dsi_remove(struct platform_device *pdev) | ||
1464 | { | ||
1465 | struct exynos_dsi *dsi = exynos_dsi_display.ctx; | ||
1466 | |||
1467 | exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF); | ||
1468 | |||
1469 | exynos_drm_display_unregister(&exynos_dsi_display); | ||
1470 | mipi_dsi_host_unregister(&dsi->dsi_host); | ||
1471 | |||
1472 | return 0; | ||
1473 | } | ||
1474 | |||
1475 | #if CONFIG_PM_SLEEP | ||
1476 | static int exynos_dsi_resume(struct device *dev) | ||
1477 | { | ||
1478 | struct exynos_dsi *dsi = exynos_dsi_display.ctx; | ||
1479 | |||
1480 | if (dsi->state & DSIM_STATE_ENABLED) { | ||
1481 | dsi->state &= ~DSIM_STATE_ENABLED; | ||
1482 | exynos_dsi_enable(dsi); | ||
1483 | } | ||
1484 | |||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | static int exynos_dsi_suspend(struct device *dev) | ||
1489 | { | ||
1490 | struct exynos_dsi *dsi = exynos_dsi_display.ctx; | ||
1491 | |||
1492 | if (dsi->state & DSIM_STATE_ENABLED) { | ||
1493 | exynos_dsi_disable(dsi); | ||
1494 | dsi->state |= DSIM_STATE_ENABLED; | ||
1495 | } | ||
1496 | |||
1497 | return 0; | ||
1498 | } | ||
1499 | #endif | ||
1500 | |||
1501 | static const struct dev_pm_ops exynos_dsi_pm_ops = { | ||
1502 | SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) | ||
1503 | }; | ||
1504 | |||
1505 | static struct of_device_id exynos_dsi_of_match[] = { | ||
1506 | { .compatible = "samsung,exynos4210-mipi-dsi" }, | ||
1507 | { } | ||
1508 | }; | ||
1509 | |||
1510 | struct platform_driver dsi_driver = { | ||
1511 | .probe = exynos_dsi_probe, | ||
1512 | .remove = exynos_dsi_remove, | ||
1513 | .driver = { | ||
1514 | .name = "exynos-dsi", | ||
1515 | .owner = THIS_MODULE, | ||
1516 | .pm = &exynos_dsi_pm_ops, | ||
1517 | .of_match_table = exynos_dsi_of_match, | ||
1518 | }, | ||
1519 | }; | ||
1520 | |||
1521 | MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>"); | ||
1522 | MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); | ||
1523 | MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master"); | ||
1524 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 5fa342e5f963..addbf7536da4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c | |||
@@ -237,6 +237,24 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { | |||
237 | .fb_probe = exynos_drm_fbdev_create, | 237 | .fb_probe = exynos_drm_fbdev_create, |
238 | }; | 238 | }; |
239 | 239 | ||
240 | bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev) | ||
241 | { | ||
242 | struct drm_connector *connector; | ||
243 | bool ret = false; | ||
244 | |||
245 | mutex_lock(&dev->mode_config.mutex); | ||
246 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
247 | if (connector->status != connector_status_connected) | ||
248 | continue; | ||
249 | |||
250 | ret = true; | ||
251 | break; | ||
252 | } | ||
253 | mutex_unlock(&dev->mode_config.mutex); | ||
254 | |||
255 | return ret; | ||
256 | } | ||
257 | |||
240 | int exynos_drm_fbdev_init(struct drm_device *dev) | 258 | int exynos_drm_fbdev_init(struct drm_device *dev) |
241 | { | 259 | { |
242 | struct exynos_drm_fbdev *fbdev; | 260 | struct exynos_drm_fbdev *fbdev; |
@@ -248,6 +266,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev) | |||
248 | if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) | 266 | if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) |
249 | return 0; | 267 | return 0; |
250 | 268 | ||
269 | if (!exynos_drm_fbdev_is_anything_connected(dev)) | ||
270 | return 0; | ||
271 | |||
251 | fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); | 272 | fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); |
252 | if (!fbdev) | 273 | if (!fbdev) |
253 | return -ENOMEM; | 274 | return -ENOMEM; |
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 3e0f13d1bc84..4ec874da5668 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig | |||
@@ -16,4 +16,18 @@ config DRM_PANEL_SIMPLE | |||
16 | that it can be automatically turned off when the panel goes into a | 16 | that it can be automatically turned off when the panel goes into a |
17 | low power state. | 17 | low power state. |
18 | 18 | ||
19 | config DRM_PANEL_LD9040 | ||
20 | tristate "LD9040 RGB/SPI panel" | ||
21 | depends on DRM && DRM_PANEL | ||
22 | depends on OF | ||
23 | select SPI | ||
24 | select VIDEOMODE_HELPERS | ||
25 | |||
26 | config DRM_PANEL_S6E8AA0 | ||
27 | tristate "S6E8AA0 DSI video mode panel" | ||
28 | depends on DRM && DRM_PANEL | ||
29 | depends on OF | ||
30 | select DRM_MIPI_DSI | ||
31 | select VIDEOMODE_HELPERS | ||
32 | |||
19 | endmenu | 33 | endmenu |
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index af9dfa235b94..8b929212fad7 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile | |||
@@ -1 +1,3 @@ | |||
1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o | 1 | obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o |
2 | obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o | ||
3 | obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o | ||
diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c new file mode 100644 index 000000000000..1f1f8371a199 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ld9040.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* | ||
2 | * ld9040 AMOLED LCD drm_panel driver. | ||
3 | * | ||
4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd | ||
5 | * Derived from drivers/video/backlight/ld9040.c | ||
6 | * | ||
7 | * Andrzej Hajda <a.hajda@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <drm/drmP.h> | ||
15 | #include <drm/drm_panel.h> | ||
16 | |||
17 | #include <linux/gpio/consumer.h> | ||
18 | #include <linux/regulator/consumer.h> | ||
19 | #include <linux/spi/spi.h> | ||
20 | |||
21 | #include <video/mipi_display.h> | ||
22 | #include <video/of_videomode.h> | ||
23 | #include <video/videomode.h> | ||
24 | |||
25 | /* Manufacturer Command Set */ | ||
26 | #define MCS_MANPWR 0xb0 | ||
27 | #define MCS_ELVSS_ON 0xb1 | ||
28 | #define MCS_USER_SETTING 0xf0 | ||
29 | #define MCS_DISPCTL 0xf2 | ||
30 | #define MCS_GTCON 0xf7 | ||
31 | #define MCS_PANEL_CONDITION 0xf8 | ||
32 | #define MCS_GAMMA_SET1 0xf9 | ||
33 | #define MCS_GAMMA_CTRL 0xfb | ||
34 | |||
35 | /* array of gamma tables for gamma value 2.2 */ | ||
36 | static u8 const ld9040_gammas[25][22] = { | ||
37 | { 0xf9, 0x00, 0x13, 0xb2, 0xba, 0xd2, 0x00, 0x30, 0x00, 0xaf, 0xc0, | ||
38 | 0xb8, 0xcd, 0x00, 0x3d, 0x00, 0xa8, 0xb8, 0xb7, 0xcd, 0x00, 0x44 }, | ||
39 | { 0xf9, 0x00, 0x13, 0xb9, 0xb9, 0xd0, 0x00, 0x3c, 0x00, 0xaf, 0xbf, | ||
40 | 0xb6, 0xcb, 0x00, 0x4b, 0x00, 0xa8, 0xb9, 0xb5, 0xcc, 0x00, 0x52 }, | ||
41 | { 0xf9, 0x00, 0x13, 0xba, 0xb9, 0xcd, 0x00, 0x41, 0x00, 0xb0, 0xbe, | ||
42 | 0xb5, 0xc9, 0x00, 0x51, 0x00, 0xa9, 0xb9, 0xb5, 0xca, 0x00, 0x57 }, | ||
43 | { 0xf9, 0x00, 0x13, 0xb9, 0xb8, 0xcd, 0x00, 0x46, 0x00, 0xb1, 0xbc, | ||
44 | 0xb5, 0xc8, 0x00, 0x56, 0x00, 0xaa, 0xb8, 0xb4, 0xc9, 0x00, 0x5d }, | ||
45 | { 0xf9, 0x00, 0x13, 0xba, 0xb8, 0xcb, 0x00, 0x4b, 0x00, 0xb3, 0xbc, | ||
46 | 0xb4, 0xc7, 0x00, 0x5c, 0x00, 0xac, 0xb8, 0xb4, 0xc8, 0x00, 0x62 }, | ||
47 | { 0xf9, 0x00, 0x13, 0xbb, 0xb7, 0xca, 0x00, 0x4f, 0x00, 0xb4, 0xbb, | ||
48 | 0xb3, 0xc7, 0x00, 0x60, 0x00, 0xad, 0xb8, 0xb4, 0xc7, 0x00, 0x67 }, | ||
49 | { 0xf9, 0x00, 0x47, 0xba, 0xb6, 0xca, 0x00, 0x53, 0x00, 0xb5, 0xbb, | ||
50 | 0xb3, 0xc6, 0x00, 0x65, 0x00, 0xae, 0xb8, 0xb3, 0xc7, 0x00, 0x6c }, | ||
51 | { 0xf9, 0x00, 0x71, 0xbb, 0xb5, 0xc8, 0x00, 0x57, 0x00, 0xb5, 0xbb, | ||
52 | 0xb0, 0xc5, 0x00, 0x6a, 0x00, 0xae, 0xb9, 0xb1, 0xc6, 0x00, 0x70 }, | ||
53 | { 0xf9, 0x00, 0x7b, 0xbb, 0xb4, 0xc8, 0x00, 0x5b, 0x00, 0xb5, 0xba, | ||
54 | 0xb1, 0xc4, 0x00, 0x6e, 0x00, 0xae, 0xb9, 0xb0, 0xc5, 0x00, 0x75 }, | ||
55 | { 0xf9, 0x00, 0x82, 0xba, 0xb4, 0xc7, 0x00, 0x5f, 0x00, 0xb5, 0xba, | ||
56 | 0xb0, 0xc3, 0x00, 0x72, 0x00, 0xae, 0xb8, 0xb0, 0xc3, 0x00, 0x7a }, | ||
57 | { 0xf9, 0x00, 0x89, 0xba, 0xb3, 0xc8, 0x00, 0x62, 0x00, 0xb6, 0xba, | ||
58 | 0xaf, 0xc3, 0x00, 0x76, 0x00, 0xaf, 0xb7, 0xae, 0xc4, 0x00, 0x7e }, | ||
59 | { 0xf9, 0x00, 0x8b, 0xb9, 0xb3, 0xc7, 0x00, 0x65, 0x00, 0xb7, 0xb8, | ||
60 | 0xaf, 0xc3, 0x00, 0x7a, 0x00, 0x80, 0xb6, 0xae, 0xc4, 0x00, 0x81 }, | ||
61 | { 0xf9, 0x00, 0x93, 0xba, 0xb3, 0xc5, 0x00, 0x69, 0x00, 0xb8, 0xb9, | ||
62 | 0xae, 0xc1, 0x00, 0x7f, 0x00, 0xb0, 0xb6, 0xae, 0xc3, 0x00, 0x85 }, | ||
63 | { 0xf9, 0x00, 0x97, 0xba, 0xb2, 0xc5, 0x00, 0x6c, 0x00, 0xb8, 0xb8, | ||
64 | 0xae, 0xc1, 0x00, 0x82, 0x00, 0xb0, 0xb6, 0xae, 0xc2, 0x00, 0x89 }, | ||
65 | { 0xf9, 0x00, 0x9a, 0xba, 0xb1, 0xc4, 0x00, 0x6f, 0x00, 0xb8, 0xb8, | ||
66 | 0xad, 0xc0, 0x00, 0x86, 0x00, 0xb0, 0xb7, 0xad, 0xc0, 0x00, 0x8d }, | ||
67 | { 0xf9, 0x00, 0x9c, 0xb9, 0xb0, 0xc4, 0x00, 0x72, 0x00, 0xb8, 0xb8, | ||
68 | 0xac, 0xbf, 0x00, 0x8a, 0x00, 0xb0, 0xb6, 0xac, 0xc0, 0x00, 0x91 }, | ||
69 | { 0xf9, 0x00, 0x9e, 0xba, 0xb0, 0xc2, 0x00, 0x75, 0x00, 0xb9, 0xb8, | ||
70 | 0xab, 0xbe, 0x00, 0x8e, 0x00, 0xb0, 0xb6, 0xac, 0xbf, 0x00, 0x94 }, | ||
71 | { 0xf9, 0x00, 0xa0, 0xb9, 0xaf, 0xc3, 0x00, 0x77, 0x00, 0xb9, 0xb7, | ||
72 | 0xab, 0xbe, 0x00, 0x90, 0x00, 0xb0, 0xb6, 0xab, 0xbf, 0x00, 0x97 }, | ||
73 | { 0xf9, 0x00, 0xa2, 0xb9, 0xaf, 0xc2, 0x00, 0x7a, 0x00, 0xb9, 0xb7, | ||
74 | 0xaa, 0xbd, 0x00, 0x94, 0x00, 0xb0, 0xb5, 0xab, 0xbf, 0x00, 0x9a }, | ||
75 | { 0xf9, 0x00, 0xa4, 0xb9, 0xaf, 0xc1, 0x00, 0x7d, 0x00, 0xb9, 0xb6, | ||
76 | 0xaa, 0xbb, 0x00, 0x97, 0x00, 0xb1, 0xb5, 0xaa, 0xbf, 0x00, 0x9d }, | ||
77 | { 0xf9, 0x00, 0xa4, 0xb8, 0xb0, 0xbf, 0x00, 0x80, 0x00, 0xb8, 0xb6, | ||
78 | 0xaa, 0xbc, 0x00, 0x9a, 0x00, 0xb0, 0xb5, 0xab, 0xbd, 0x00, 0xa0 }, | ||
79 | { 0xf9, 0x00, 0xa8, 0xb8, 0xae, 0xbe, 0x00, 0x84, 0x00, 0xb9, 0xb7, | ||
80 | 0xa8, 0xbc, 0x00, 0x9d, 0x00, 0xb2, 0xb5, 0xaa, 0xbc, 0x00, 0xa4 }, | ||
81 | { 0xf9, 0x00, 0xa9, 0xb6, 0xad, 0xbf, 0x00, 0x86, 0x00, 0xb8, 0xb5, | ||
82 | 0xa8, 0xbc, 0x00, 0xa0, 0x00, 0xb3, 0xb3, 0xa9, 0xbc, 0x00, 0xa7 }, | ||
83 | { 0xf9, 0x00, 0xa9, 0xb7, 0xae, 0xbd, 0x00, 0x89, 0x00, 0xb7, 0xb6, | ||
84 | 0xa8, 0xba, 0x00, 0xa4, 0x00, 0xb1, 0xb4, 0xaa, 0xbb, 0x00, 0xaa }, | ||
85 | { 0xf9, 0x00, 0xa7, 0xb4, 0xae, 0xbf, 0x00, 0x91, 0x00, 0xb2, 0xb4, | ||
86 | 0xaa, 0xbb, 0x00, 0xac, 0x00, 0xb3, 0xb1, 0xaa, 0xbc, 0x00, 0xb3 }, | ||
87 | }; | ||
88 | |||
89 | struct ld9040 { | ||
90 | struct device *dev; | ||
91 | struct drm_panel panel; | ||
92 | |||
93 | struct regulator_bulk_data supplies[2]; | ||
94 | struct gpio_desc *reset_gpio; | ||
95 | u32 power_on_delay; | ||
96 | u32 reset_delay; | ||
97 | struct videomode vm; | ||
98 | u32 width_mm; | ||
99 | u32 height_mm; | ||
100 | |||
101 | int brightness; | ||
102 | |||
103 | /* This field is tested by functions directly accessing bus before | ||
104 | * transfer, transfer is skipped if it is set. In case of transfer | ||
105 | * failure or unexpected response the field is set to error value. | ||
106 | * Such construct allows to eliminate many checks in higher level | ||
107 | * functions. | ||
108 | */ | ||
109 | int error; | ||
110 | }; | ||
111 | |||
112 | #define panel_to_ld9040(p) container_of(p, struct ld9040, panel) | ||
113 | |||
114 | static int ld9040_clear_error(struct ld9040 *ctx) | ||
115 | { | ||
116 | int ret = ctx->error; | ||
117 | |||
118 | ctx->error = 0; | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static int ld9040_spi_write_word(struct ld9040 *ctx, u16 data) | ||
123 | { | ||
124 | struct spi_device *spi = to_spi_device(ctx->dev); | ||
125 | struct spi_transfer xfer = { | ||
126 | .len = 2, | ||
127 | .tx_buf = &data, | ||
128 | }; | ||
129 | struct spi_message msg; | ||
130 | |||
131 | spi_message_init(&msg); | ||
132 | spi_message_add_tail(&xfer, &msg); | ||
133 | |||
134 | return spi_sync(spi, &msg); | ||
135 | } | ||
136 | |||
137 | static void ld9040_dcs_write(struct ld9040 *ctx, const u8 *data, size_t len) | ||
138 | { | ||
139 | int ret = 0; | ||
140 | |||
141 | if (ctx->error < 0 || len == 0) | ||
142 | return; | ||
143 | |||
144 | dev_dbg(ctx->dev, "writing dcs seq: %*ph\n", len, data); | ||
145 | ret = ld9040_spi_write_word(ctx, *data); | ||
146 | |||
147 | while (!ret && --len) { | ||
148 | ++data; | ||
149 | ret = ld9040_spi_write_word(ctx, *data | 0x100); | ||
150 | } | ||
151 | |||
152 | if (ret) { | ||
153 | dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, | ||
154 | data); | ||
155 | ctx->error = ret; | ||
156 | } | ||
157 | |||
158 | usleep_range(300, 310); | ||
159 | } | ||
160 | |||
161 | #define ld9040_dcs_write_seq_static(ctx, seq...) \ | ||
162 | ({\ | ||
163 | static const u8 d[] = { seq };\ | ||
164 | ld9040_dcs_write(ctx, d, ARRAY_SIZE(d));\ | ||
165 | }) | ||
166 | |||
167 | static void ld9040_brightness_set(struct ld9040 *ctx) | ||
168 | { | ||
169 | ld9040_dcs_write(ctx, ld9040_gammas[ctx->brightness], | ||
170 | ARRAY_SIZE(ld9040_gammas[ctx->brightness])); | ||
171 | |||
172 | ld9040_dcs_write_seq_static(ctx, MCS_GAMMA_CTRL, 0x02, 0x5a); | ||
173 | } | ||
174 | |||
175 | static void ld9040_init(struct ld9040 *ctx) | ||
176 | { | ||
177 | ld9040_dcs_write_seq_static(ctx, MCS_USER_SETTING, 0x5a, 0x5a); | ||
178 | ld9040_dcs_write_seq_static(ctx, MCS_PANEL_CONDITION, | ||
179 | 0x05, 0x65, 0x96, 0x71, 0x7d, 0x19, 0x3b, 0x0d, | ||
180 | 0x19, 0x7e, 0x0d, 0xe2, 0x00, 0x00, 0x7e, 0x7d, | ||
181 | 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02); | ||
182 | ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL, | ||
183 | 0x02, 0x08, 0x08, 0x10, 0x10); | ||
184 | ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04); | ||
185 | ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16); | ||
186 | ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00); | ||
187 | ld9040_brightness_set(ctx); | ||
188 | ld9040_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); | ||
189 | ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); | ||
190 | } | ||
191 | |||
192 | static int ld9040_power_on(struct ld9040 *ctx) | ||
193 | { | ||
194 | int ret; | ||
195 | |||
196 | ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
197 | if (ret < 0) | ||
198 | return ret; | ||
199 | |||
200 | msleep(ctx->power_on_delay); | ||
201 | gpiod_set_value(ctx->reset_gpio, 0); | ||
202 | msleep(ctx->reset_delay); | ||
203 | gpiod_set_value(ctx->reset_gpio, 1); | ||
204 | msleep(ctx->reset_delay); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int ld9040_power_off(struct ld9040 *ctx) | ||
210 | { | ||
211 | return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
212 | } | ||
213 | |||
214 | static int ld9040_disable(struct drm_panel *panel) | ||
215 | { | ||
216 | struct ld9040 *ctx = panel_to_ld9040(panel); | ||
217 | |||
218 | msleep(120); | ||
219 | ld9040_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); | ||
220 | ld9040_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); | ||
221 | msleep(40); | ||
222 | |||
223 | ld9040_clear_error(ctx); | ||
224 | |||
225 | return ld9040_power_off(ctx); | ||
226 | } | ||
227 | |||
228 | static int ld9040_enable(struct drm_panel *panel) | ||
229 | { | ||
230 | struct ld9040 *ctx = panel_to_ld9040(panel); | ||
231 | int ret; | ||
232 | |||
233 | ret = ld9040_power_on(ctx); | ||
234 | if (ret < 0) | ||
235 | return ret; | ||
236 | |||
237 | ld9040_init(ctx); | ||
238 | |||
239 | ret = ld9040_clear_error(ctx); | ||
240 | |||
241 | if (ret < 0) | ||
242 | ld9040_disable(panel); | ||
243 | |||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static int ld9040_get_modes(struct drm_panel *panel) | ||
248 | { | ||
249 | struct drm_connector *connector = panel->connector; | ||
250 | struct ld9040 *ctx = panel_to_ld9040(panel); | ||
251 | struct drm_display_mode *mode; | ||
252 | |||
253 | mode = drm_mode_create(connector->dev); | ||
254 | if (!mode) { | ||
255 | DRM_ERROR("failed to create a new display mode\n"); | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | drm_display_mode_from_videomode(&ctx->vm, mode); | ||
260 | mode->width_mm = ctx->width_mm; | ||
261 | mode->height_mm = ctx->height_mm; | ||
262 | connector->display_info.width_mm = mode->width_mm; | ||
263 | connector->display_info.height_mm = mode->height_mm; | ||
264 | |||
265 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
266 | drm_mode_probed_add(connector, mode); | ||
267 | |||
268 | return 1; | ||
269 | } | ||
270 | |||
271 | static const struct drm_panel_funcs ld9040_drm_funcs = { | ||
272 | .disable = ld9040_disable, | ||
273 | .enable = ld9040_enable, | ||
274 | .get_modes = ld9040_get_modes, | ||
275 | }; | ||
276 | |||
277 | static int ld9040_parse_dt(struct ld9040 *ctx) | ||
278 | { | ||
279 | struct device *dev = ctx->dev; | ||
280 | struct device_node *np = dev->of_node; | ||
281 | int ret; | ||
282 | |||
283 | ret = of_get_videomode(np, &ctx->vm, 0); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | |||
287 | of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay); | ||
288 | of_property_read_u32(np, "reset-delay", &ctx->reset_delay); | ||
289 | of_property_read_u32(np, "panel-width-mm", &ctx->width_mm); | ||
290 | of_property_read_u32(np, "panel-height-mm", &ctx->height_mm); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int ld9040_probe(struct spi_device *spi) | ||
296 | { | ||
297 | struct device *dev = &spi->dev; | ||
298 | struct ld9040 *ctx; | ||
299 | int ret; | ||
300 | |||
301 | ctx = devm_kzalloc(dev, sizeof(struct ld9040), GFP_KERNEL); | ||
302 | if (!ctx) | ||
303 | return -ENOMEM; | ||
304 | |||
305 | spi_set_drvdata(spi, ctx); | ||
306 | |||
307 | ctx->dev = dev; | ||
308 | ctx->brightness = ARRAY_SIZE(ld9040_gammas) - 1; | ||
309 | |||
310 | ret = ld9040_parse_dt(ctx); | ||
311 | if (ret < 0) | ||
312 | return ret; | ||
313 | |||
314 | ctx->supplies[0].supply = "vdd3"; | ||
315 | ctx->supplies[1].supply = "vci"; | ||
316 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), | ||
317 | ctx->supplies); | ||
318 | if (ret < 0) | ||
319 | return ret; | ||
320 | |||
321 | ctx->reset_gpio = devm_gpiod_get(dev, "reset"); | ||
322 | if (IS_ERR(ctx->reset_gpio)) { | ||
323 | dev_err(dev, "cannot get reset-gpios %ld\n", | ||
324 | PTR_ERR(ctx->reset_gpio)); | ||
325 | return PTR_ERR(ctx->reset_gpio); | ||
326 | } | ||
327 | ret = gpiod_direction_output(ctx->reset_gpio, 1); | ||
328 | if (ret < 0) { | ||
329 | dev_err(dev, "cannot configure reset-gpios %d\n", ret); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | spi->bits_per_word = 9; | ||
334 | ret = spi_setup(spi); | ||
335 | if (ret < 0) { | ||
336 | dev_err(dev, "spi setup failed.\n"); | ||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | drm_panel_init(&ctx->panel); | ||
341 | ctx->panel.dev = dev; | ||
342 | ctx->panel.funcs = &ld9040_drm_funcs; | ||
343 | |||
344 | return drm_panel_add(&ctx->panel); | ||
345 | } | ||
346 | |||
347 | static int ld9040_remove(struct spi_device *spi) | ||
348 | { | ||
349 | struct ld9040 *ctx = spi_get_drvdata(spi); | ||
350 | |||
351 | ld9040_power_off(ctx); | ||
352 | drm_panel_remove(&ctx->panel); | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static struct of_device_id ld9040_of_match[] = { | ||
358 | { .compatible = "samsung,ld9040" }, | ||
359 | { } | ||
360 | }; | ||
361 | MODULE_DEVICE_TABLE(of, ld9040_of_match); | ||
362 | |||
363 | static struct spi_driver ld9040_driver = { | ||
364 | .probe = ld9040_probe, | ||
365 | .remove = ld9040_remove, | ||
366 | .driver = { | ||
367 | .name = "ld9040", | ||
368 | .owner = THIS_MODULE, | ||
369 | .of_match_table = ld9040_of_match, | ||
370 | }, | ||
371 | }; | ||
372 | module_spi_driver(ld9040_driver); | ||
373 | |||
374 | MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); | ||
375 | MODULE_DESCRIPTION("ld9040 LCD Driver"); | ||
376 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c new file mode 100644 index 000000000000..35941d2412b8 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c | |||
@@ -0,0 +1,1069 @@ | |||
1 | /* | ||
2 | * MIPI-DSI based s6e8aa0 AMOLED LCD 5.3 inch panel driver. | ||
3 | * | ||
4 | * Copyright (c) 2013 Samsung Electronics Co., Ltd | ||
5 | * | ||
6 | * Inki Dae, <inki.dae@samsung.com> | ||
7 | * Donghwa Lee, <dh09.lee@samsung.com> | ||
8 | * Joongmock Shin <jmock.shin@samsung.com> | ||
9 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
10 | * Tomasz Figa <t.figa@samsung.com> | ||
11 | * Andrzej Hajda <a.hajda@samsung.com> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | */ | ||
17 | |||
18 | #include <drm/drmP.h> | ||
19 | #include <drm/drm_mipi_dsi.h> | ||
20 | #include <drm/drm_panel.h> | ||
21 | |||
22 | #include <linux/gpio/consumer.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | |||
25 | #include <video/mipi_display.h> | ||
26 | #include <video/of_videomode.h> | ||
27 | #include <video/videomode.h> | ||
28 | |||
29 | #define LDI_MTP_LENGTH 24 | ||
30 | #define GAMMA_LEVEL_NUM 25 | ||
31 | #define GAMMA_TABLE_LEN 26 | ||
32 | |||
33 | #define PANELCTL_SS_MASK (1 << 5) | ||
34 | #define PANELCTL_SS_1_800 (0 << 5) | ||
35 | #define PANELCTL_SS_800_1 (1 << 5) | ||
36 | #define PANELCTL_GTCON_MASK (7 << 2) | ||
37 | #define PANELCTL_GTCON_110 (6 << 2) | ||
38 | #define PANELCTL_GTCON_111 (7 << 2) | ||
39 | |||
40 | #define PANELCTL_CLK1_CON_MASK (7 << 3) | ||
41 | #define PANELCTL_CLK1_000 (0 << 3) | ||
42 | #define PANELCTL_CLK1_001 (1 << 3) | ||
43 | #define PANELCTL_CLK2_CON_MASK (7 << 0) | ||
44 | #define PANELCTL_CLK2_000 (0 << 0) | ||
45 | #define PANELCTL_CLK2_001 (1 << 0) | ||
46 | |||
47 | #define PANELCTL_INT1_CON_MASK (7 << 3) | ||
48 | #define PANELCTL_INT1_000 (0 << 3) | ||
49 | #define PANELCTL_INT1_001 (1 << 3) | ||
50 | #define PANELCTL_INT2_CON_MASK (7 << 0) | ||
51 | #define PANELCTL_INT2_000 (0 << 0) | ||
52 | #define PANELCTL_INT2_001 (1 << 0) | ||
53 | |||
54 | #define PANELCTL_BICTL_CON_MASK (7 << 3) | ||
55 | #define PANELCTL_BICTL_000 (0 << 3) | ||
56 | #define PANELCTL_BICTL_001 (1 << 3) | ||
57 | #define PANELCTL_BICTLB_CON_MASK (7 << 0) | ||
58 | #define PANELCTL_BICTLB_000 (0 << 0) | ||
59 | #define PANELCTL_BICTLB_001 (1 << 0) | ||
60 | |||
61 | #define PANELCTL_EM_CLK1_CON_MASK (7 << 3) | ||
62 | #define PANELCTL_EM_CLK1_110 (6 << 3) | ||
63 | #define PANELCTL_EM_CLK1_111 (7 << 3) | ||
64 | #define PANELCTL_EM_CLK1B_CON_MASK (7 << 0) | ||
65 | #define PANELCTL_EM_CLK1B_110 (6 << 0) | ||
66 | #define PANELCTL_EM_CLK1B_111 (7 << 0) | ||
67 | |||
68 | #define PANELCTL_EM_CLK2_CON_MASK (7 << 3) | ||
69 | #define PANELCTL_EM_CLK2_110 (6 << 3) | ||
70 | #define PANELCTL_EM_CLK2_111 (7 << 3) | ||
71 | #define PANELCTL_EM_CLK2B_CON_MASK (7 << 0) | ||
72 | #define PANELCTL_EM_CLK2B_110 (6 << 0) | ||
73 | #define PANELCTL_EM_CLK2B_111 (7 << 0) | ||
74 | |||
75 | #define PANELCTL_EM_INT1_CON_MASK (7 << 3) | ||
76 | #define PANELCTL_EM_INT1_000 (0 << 3) | ||
77 | #define PANELCTL_EM_INT1_001 (1 << 3) | ||
78 | #define PANELCTL_EM_INT2_CON_MASK (7 << 0) | ||
79 | #define PANELCTL_EM_INT2_000 (0 << 0) | ||
80 | #define PANELCTL_EM_INT2_001 (1 << 0) | ||
81 | |||
82 | #define AID_DISABLE (0x4) | ||
83 | #define AID_1 (0x5) | ||
84 | #define AID_2 (0x6) | ||
85 | #define AID_3 (0x7) | ||
86 | |||
87 | typedef u8 s6e8aa0_gamma_table[GAMMA_TABLE_LEN]; | ||
88 | |||
89 | struct s6e8aa0_variant { | ||
90 | u8 version; | ||
91 | const s6e8aa0_gamma_table *gamma_tables; | ||
92 | }; | ||
93 | |||
94 | struct s6e8aa0 { | ||
95 | struct device *dev; | ||
96 | struct drm_panel panel; | ||
97 | |||
98 | struct regulator_bulk_data supplies[2]; | ||
99 | struct gpio_desc *reset_gpio; | ||
100 | u32 power_on_delay; | ||
101 | u32 reset_delay; | ||
102 | u32 init_delay; | ||
103 | bool flip_horizontal; | ||
104 | bool flip_vertical; | ||
105 | struct videomode vm; | ||
106 | u32 width_mm; | ||
107 | u32 height_mm; | ||
108 | |||
109 | u8 version; | ||
110 | u8 id; | ||
111 | const struct s6e8aa0_variant *variant; | ||
112 | int brightness; | ||
113 | |||
114 | /* This field is tested by functions directly accessing DSI bus before | ||
115 | * transfer, transfer is skipped if it is set. In case of transfer | ||
116 | * failure or unexpected response the field is set to error value. | ||
117 | * Such construct allows to eliminate many checks in higher level | ||
118 | * functions. | ||
119 | */ | ||
120 | int error; | ||
121 | }; | ||
122 | |||
123 | #define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel) | ||
124 | |||
125 | static int s6e8aa0_clear_error(struct s6e8aa0 *ctx) | ||
126 | { | ||
127 | int ret = ctx->error; | ||
128 | |||
129 | ctx->error = 0; | ||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len) | ||
134 | { | ||
135 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | ||
136 | int ret; | ||
137 | |||
138 | if (ctx->error < 0) | ||
139 | return; | ||
140 | |||
141 | ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len); | ||
142 | if (ret < 0) { | ||
143 | dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len, | ||
144 | data); | ||
145 | ctx->error = ret; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len) | ||
150 | { | ||
151 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | ||
152 | int ret; | ||
153 | |||
154 | if (ctx->error < 0) | ||
155 | return ctx->error; | ||
156 | |||
157 | ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); | ||
158 | if (ret < 0) { | ||
159 | dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd); | ||
160 | ctx->error = ret; | ||
161 | } | ||
162 | |||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | #define s6e8aa0_dcs_write_seq(ctx, seq...) \ | ||
167 | ({\ | ||
168 | const u8 d[] = { seq };\ | ||
169 | BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too big for stack");\ | ||
170 | s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\ | ||
171 | }) | ||
172 | |||
173 | #define s6e8aa0_dcs_write_seq_static(ctx, seq...) \ | ||
174 | ({\ | ||
175 | static const u8 d[] = { seq };\ | ||
176 | s6e8aa0_dcs_write(ctx, d, ARRAY_SIZE(d));\ | ||
177 | }) | ||
178 | |||
179 | static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *ctx) | ||
180 | { | ||
181 | s6e8aa0_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a); | ||
182 | } | ||
183 | |||
184 | static void s6e8aa0_panel_cond_set_v142(struct s6e8aa0 *ctx) | ||
185 | { | ||
186 | static const u8 aids[] = { | ||
187 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x60, 0x80, 0xA0 | ||
188 | }; | ||
189 | u8 aid = aids[ctx->id >> 5]; | ||
190 | u8 cfg = 0x3d; | ||
191 | u8 clk_con = 0xc8; | ||
192 | u8 int_con = 0x08; | ||
193 | u8 bictl_con = 0x48; | ||
194 | u8 em_clk1_con = 0xff; | ||
195 | u8 em_clk2_con = 0xff; | ||
196 | u8 em_int_con = 0xc8; | ||
197 | |||
198 | if (ctx->flip_vertical) { | ||
199 | /* GTCON */ | ||
200 | cfg &= ~(PANELCTL_GTCON_MASK); | ||
201 | cfg |= (PANELCTL_GTCON_110); | ||
202 | } | ||
203 | |||
204 | if (ctx->flip_horizontal) { | ||
205 | /* SS */ | ||
206 | cfg &= ~(PANELCTL_SS_MASK); | ||
207 | cfg |= (PANELCTL_SS_1_800); | ||
208 | } | ||
209 | |||
210 | if (ctx->flip_horizontal || ctx->flip_vertical) { | ||
211 | /* CLK1,2_CON */ | ||
212 | clk_con &= ~(PANELCTL_CLK1_CON_MASK | | ||
213 | PANELCTL_CLK2_CON_MASK); | ||
214 | clk_con |= (PANELCTL_CLK1_000 | PANELCTL_CLK2_001); | ||
215 | |||
216 | /* INT1,2_CON */ | ||
217 | int_con &= ~(PANELCTL_INT1_CON_MASK | | ||
218 | PANELCTL_INT2_CON_MASK); | ||
219 | int_con |= (PANELCTL_INT1_000 | PANELCTL_INT2_001); | ||
220 | |||
221 | /* BICTL,B_CON */ | ||
222 | bictl_con &= ~(PANELCTL_BICTL_CON_MASK | | ||
223 | PANELCTL_BICTLB_CON_MASK); | ||
224 | bictl_con |= (PANELCTL_BICTL_000 | | ||
225 | PANELCTL_BICTLB_001); | ||
226 | |||
227 | /* EM_CLK1,1B_CON */ | ||
228 | em_clk1_con &= ~(PANELCTL_EM_CLK1_CON_MASK | | ||
229 | PANELCTL_EM_CLK1B_CON_MASK); | ||
230 | em_clk1_con |= (PANELCTL_EM_CLK1_110 | | ||
231 | PANELCTL_EM_CLK1B_110); | ||
232 | |||
233 | /* EM_CLK2,2B_CON */ | ||
234 | em_clk2_con &= ~(PANELCTL_EM_CLK2_CON_MASK | | ||
235 | PANELCTL_EM_CLK2B_CON_MASK); | ||
236 | em_clk2_con |= (PANELCTL_EM_CLK2_110 | | ||
237 | PANELCTL_EM_CLK2B_110); | ||
238 | |||
239 | /* EM_INT1,2_CON */ | ||
240 | em_int_con &= ~(PANELCTL_EM_INT1_CON_MASK | | ||
241 | PANELCTL_EM_INT2_CON_MASK); | ||
242 | em_int_con |= (PANELCTL_EM_INT1_000 | | ||
243 | PANELCTL_EM_INT2_001); | ||
244 | } | ||
245 | |||
246 | s6e8aa0_dcs_write_seq(ctx, | ||
247 | 0xf8, cfg, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, | ||
248 | 0x3c, 0x78, 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, | ||
249 | 0x00, 0x20, aid, 0x08, 0x6e, 0x00, 0x00, 0x00, | ||
250 | 0x02, 0x07, 0x07, 0x23, 0x23, 0xc0, clk_con, int_con, | ||
251 | bictl_con, 0xc1, 0x00, 0xc1, em_clk1_con, em_clk2_con, | ||
252 | em_int_con); | ||
253 | } | ||
254 | |||
255 | static void s6e8aa0_panel_cond_set(struct s6e8aa0 *ctx) | ||
256 | { | ||
257 | if (ctx->version < 142) | ||
258 | s6e8aa0_dcs_write_seq_static(ctx, | ||
259 | 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x94, 0x00, | ||
260 | 0x3c, 0x78, 0x10, 0x27, 0x08, 0x6e, 0x00, 0x00, | ||
261 | 0x00, 0x00, 0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, | ||
262 | 0x00, 0x07, 0x07, 0x23, 0x6e, 0xc0, 0xc1, 0x01, | ||
263 | 0x81, 0xc1, 0x00, 0xc3, 0xf6, 0xf6, 0xc1 | ||
264 | ); | ||
265 | else | ||
266 | s6e8aa0_panel_cond_set_v142(ctx); | ||
267 | } | ||
268 | |||
269 | static void s6e8aa0_display_condition_set(struct s6e8aa0 *ctx) | ||
270 | { | ||
271 | s6e8aa0_dcs_write_seq_static(ctx, 0xf2, 0x80, 0x03, 0x0d); | ||
272 | } | ||
273 | |||
274 | static void s6e8aa0_etc_source_control(struct s6e8aa0 *ctx) | ||
275 | { | ||
276 | s6e8aa0_dcs_write_seq_static(ctx, 0xf6, 0x00, 0x02, 0x00); | ||
277 | } | ||
278 | |||
279 | static void s6e8aa0_etc_pentile_control(struct s6e8aa0 *ctx) | ||
280 | { | ||
281 | static const u8 pent32[] = { | ||
282 | 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xc0, 0x44, 0x44, 0xc0, 0x00 | ||
283 | }; | ||
284 | |||
285 | static const u8 pent142[] = { | ||
286 | 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, 0x00 | ||
287 | }; | ||
288 | |||
289 | if (ctx->version < 142) | ||
290 | s6e8aa0_dcs_write(ctx, pent32, ARRAY_SIZE(pent32)); | ||
291 | else | ||
292 | s6e8aa0_dcs_write(ctx, pent142, ARRAY_SIZE(pent142)); | ||
293 | } | ||
294 | |||
295 | static void s6e8aa0_etc_power_control(struct s6e8aa0 *ctx) | ||
296 | { | ||
297 | static const u8 pwr142[] = { | ||
298 | 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02 | ||
299 | }; | ||
300 | |||
301 | static const u8 pwr32[] = { | ||
302 | 0xf4, 0xcf, 0x0a, 0x15, 0x10, 0x19, 0x33, 0x02 | ||
303 | }; | ||
304 | |||
305 | if (ctx->version < 142) | ||
306 | s6e8aa0_dcs_write(ctx, pwr32, ARRAY_SIZE(pwr32)); | ||
307 | else | ||
308 | s6e8aa0_dcs_write(ctx, pwr142, ARRAY_SIZE(pwr142)); | ||
309 | } | ||
310 | |||
311 | static void s6e8aa0_etc_elvss_control(struct s6e8aa0 *ctx) | ||
312 | { | ||
313 | u8 id = ctx->id ? 0 : 0x95; | ||
314 | |||
315 | s6e8aa0_dcs_write_seq(ctx, 0xb1, 0x04, id); | ||
316 | } | ||
317 | |||
318 | static void s6e8aa0_elvss_nvm_set_v142(struct s6e8aa0 *ctx) | ||
319 | { | ||
320 | u8 br; | ||
321 | |||
322 | switch (ctx->brightness) { | ||
323 | case 0 ... 6: /* 30cd ~ 100cd */ | ||
324 | br = 0xdf; | ||
325 | break; | ||
326 | case 7 ... 11: /* 120cd ~ 150cd */ | ||
327 | br = 0xdd; | ||
328 | break; | ||
329 | case 12 ... 15: /* 180cd ~ 210cd */ | ||
330 | default: | ||
331 | br = 0xd9; | ||
332 | break; | ||
333 | case 16 ... 24: /* 240cd ~ 300cd */ | ||
334 | br = 0xd0; | ||
335 | break; | ||
336 | } | ||
337 | |||
338 | s6e8aa0_dcs_write_seq(ctx, 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, | ||
339 | 0xc4, 0x0f, 0x40, 0x41, br, 0x00, 0x60, 0x19); | ||
340 | } | ||
341 | |||
342 | static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *ctx) | ||
343 | { | ||
344 | if (ctx->version < 142) | ||
345 | s6e8aa0_dcs_write_seq_static(ctx, | ||
346 | 0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x07, | ||
347 | 0x40, 0x41, 0xc1, 0x00, 0x60, 0x19); | ||
348 | else | ||
349 | s6e8aa0_elvss_nvm_set_v142(ctx); | ||
350 | }; | ||
351 | |||
352 | static void s6e8aa0_apply_level_2_key(struct s6e8aa0 *ctx) | ||
353 | { | ||
354 | s6e8aa0_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a); | ||
355 | } | ||
356 | |||
357 | static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v142[GAMMA_LEVEL_NUM] = { | ||
358 | { | ||
359 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x62, 0x55, 0x55, | ||
360 | 0xaf, 0xb1, 0xb1, 0xbd, 0xce, 0xb7, 0x9a, 0xb1, | ||
361 | 0x90, 0xb2, 0xc4, 0xae, 0x00, 0x60, 0x00, 0x40, | ||
362 | 0x00, 0x70, | ||
363 | }, { | ||
364 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x74, 0x68, 0x69, | ||
365 | 0xb8, 0xc1, 0xb7, 0xbd, 0xcd, 0xb8, 0x93, 0xab, | ||
366 | 0x88, 0xb4, 0xc4, 0xb1, 0x00, 0x6b, 0x00, 0x4d, | ||
367 | 0x00, 0x7d, | ||
368 | }, { | ||
369 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x95, 0x8a, 0x89, | ||
370 | 0xb4, 0xc6, 0xb2, 0xc5, 0xd2, 0xbf, 0x90, 0xa8, | ||
371 | 0x85, 0xb5, 0xc4, 0xb3, 0x00, 0x7b, 0x00, 0x5d, | ||
372 | 0x00, 0x8f, | ||
373 | }, { | ||
374 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9f, 0x98, 0x92, | ||
375 | 0xb3, 0xc4, 0xb0, 0xbc, 0xcc, 0xb4, 0x91, 0xa6, | ||
376 | 0x87, 0xb5, 0xc5, 0xb4, 0x00, 0x87, 0x00, 0x6a, | ||
377 | 0x00, 0x9e, | ||
378 | }, { | ||
379 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x99, 0x93, 0x8b, | ||
380 | 0xb2, 0xc2, 0xb0, 0xbd, 0xce, 0xb4, 0x90, 0xa6, | ||
381 | 0x87, 0xb3, 0xc3, 0xb2, 0x00, 0x8d, 0x00, 0x70, | ||
382 | 0x00, 0xa4, | ||
383 | }, { | ||
384 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xa5, 0x99, | ||
385 | 0xb2, 0xc2, 0xb0, 0xbb, 0xcd, 0xb1, 0x93, 0xa7, | ||
386 | 0x8a, 0xb2, 0xc1, 0xb0, 0x00, 0x92, 0x00, 0x75, | ||
387 | 0x00, 0xaa, | ||
388 | }, { | ||
389 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xa0, 0x93, | ||
390 | 0xb6, 0xc4, 0xb4, 0xb5, 0xc8, 0xaa, 0x94, 0xa9, | ||
391 | 0x8c, 0xb2, 0xc0, 0xb0, 0x00, 0x97, 0x00, 0x7a, | ||
392 | 0x00, 0xaf, | ||
393 | }, { | ||
394 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xa7, 0x96, | ||
395 | 0xb3, 0xc2, 0xb0, 0xba, 0xcb, 0xb0, 0x94, 0xa8, | ||
396 | 0x8c, 0xb0, 0xbf, 0xaf, 0x00, 0x9f, 0x00, 0x83, | ||
397 | 0x00, 0xb9, | ||
398 | }, { | ||
399 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9d, 0xa2, 0x90, | ||
400 | 0xb6, 0xc5, 0xb3, 0xb8, 0xc9, 0xae, 0x94, 0xa8, | ||
401 | 0x8d, 0xaf, 0xbd, 0xad, 0x00, 0xa4, 0x00, 0x88, | ||
402 | 0x00, 0xbf, | ||
403 | }, { | ||
404 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xac, 0x97, | ||
405 | 0xb4, 0xc4, 0xb1, 0xbb, 0xcb, 0xb2, 0x93, 0xa7, | ||
406 | 0x8d, 0xae, 0xbc, 0xad, 0x00, 0xa7, 0x00, 0x8c, | ||
407 | 0x00, 0xc3, | ||
408 | }, { | ||
409 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa2, 0xa9, 0x93, | ||
410 | 0xb6, 0xc5, 0xb2, 0xba, 0xc9, 0xb0, 0x93, 0xa7, | ||
411 | 0x8d, 0xae, 0xbb, 0xac, 0x00, 0xab, 0x00, 0x90, | ||
412 | 0x00, 0xc8, | ||
413 | }, { | ||
414 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9e, 0xa6, 0x8f, | ||
415 | 0xb7, 0xc6, 0xb3, 0xb8, 0xc8, 0xb0, 0x93, 0xa6, | ||
416 | 0x8c, 0xae, 0xbb, 0xad, 0x00, 0xae, 0x00, 0x93, | ||
417 | 0x00, 0xcc, | ||
418 | }, { | ||
419 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb4, 0x9c, | ||
420 | 0xb3, 0xc3, 0xaf, 0xb7, 0xc7, 0xaf, 0x93, 0xa6, | ||
421 | 0x8c, 0xaf, 0xbc, 0xad, 0x00, 0xb1, 0x00, 0x97, | ||
422 | 0x00, 0xcf, | ||
423 | }, { | ||
424 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xb1, 0x98, | ||
425 | 0xb1, 0xc2, 0xab, 0xba, 0xc9, 0xb2, 0x93, 0xa6, | ||
426 | 0x8d, 0xae, 0xba, 0xab, 0x00, 0xb5, 0x00, 0x9b, | ||
427 | 0x00, 0xd4, | ||
428 | }, { | ||
429 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xae, 0x94, | ||
430 | 0xb2, 0xc3, 0xac, 0xbb, 0xca, 0xb4, 0x91, 0xa4, | ||
431 | 0x8a, 0xae, 0xba, 0xac, 0x00, 0xb8, 0x00, 0x9e, | ||
432 | 0x00, 0xd8, | ||
433 | }, { | ||
434 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb7, 0x9c, | ||
435 | 0xae, 0xc0, 0xa9, 0xba, 0xc9, 0xb3, 0x92, 0xa5, | ||
436 | 0x8b, 0xad, 0xb9, 0xab, 0x00, 0xbb, 0x00, 0xa1, | ||
437 | 0x00, 0xdc, | ||
438 | }, { | ||
439 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb4, 0x97, | ||
440 | 0xb0, 0xc1, 0xaa, 0xb9, 0xc8, 0xb2, 0x92, 0xa5, | ||
441 | 0x8c, 0xae, 0xb9, 0xab, 0x00, 0xbe, 0x00, 0xa4, | ||
442 | 0x00, 0xdf, | ||
443 | }, { | ||
444 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94, | ||
445 | 0xb0, 0xc2, 0xab, 0xbb, 0xc9, 0xb3, 0x91, 0xa4, | ||
446 | 0x8b, 0xad, 0xb8, 0xaa, 0x00, 0xc1, 0x00, 0xa8, | ||
447 | 0x00, 0xe2, | ||
448 | }, { | ||
449 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94, | ||
450 | 0xae, 0xbf, 0xa8, 0xb9, 0xc8, 0xb3, 0x92, 0xa4, | ||
451 | 0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xc4, 0x00, 0xab, | ||
452 | 0x00, 0xe6, | ||
453 | }, { | ||
454 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb6, 0x98, | ||
455 | 0xaf, 0xc0, 0xa8, 0xb8, 0xc7, 0xb2, 0x93, 0xa5, | ||
456 | 0x8d, 0xad, 0xb7, 0xa9, 0x00, 0xc7, 0x00, 0xae, | ||
457 | 0x00, 0xe9, | ||
458 | }, { | ||
459 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95, | ||
460 | 0xaf, 0xc1, 0xa9, 0xb9, 0xc8, 0xb3, 0x92, 0xa4, | ||
461 | 0x8b, 0xad, 0xb7, 0xaa, 0x00, 0xc9, 0x00, 0xb0, | ||
462 | 0x00, 0xec, | ||
463 | }, { | ||
464 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95, | ||
465 | 0xac, 0xbe, 0xa6, 0xbb, 0xc9, 0xb4, 0x90, 0xa3, | ||
466 | 0x8a, 0xad, 0xb7, 0xa9, 0x00, 0xcc, 0x00, 0xb4, | ||
467 | 0x00, 0xf0, | ||
468 | }, { | ||
469 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xb0, 0x91, | ||
470 | 0xae, 0xc0, 0xa6, 0xba, 0xc8, 0xb4, 0x91, 0xa4, | ||
471 | 0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xcf, 0x00, 0xb7, | ||
472 | 0x00, 0xf3, | ||
473 | }, { | ||
474 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb8, 0x98, | ||
475 | 0xab, 0xbd, 0xa4, 0xbb, 0xc9, 0xb5, 0x91, 0xa3, | ||
476 | 0x8b, 0xac, 0xb6, 0xa8, 0x00, 0xd1, 0x00, 0xb9, | ||
477 | 0x00, 0xf6, | ||
478 | }, { | ||
479 | 0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb5, 0x95, | ||
480 | 0xa9, 0xbc, 0xa1, 0xbb, 0xc9, 0xb5, 0x91, 0xa3, | ||
481 | 0x8a, 0xad, 0xb6, 0xa8, 0x00, 0xd6, 0x00, 0xbf, | ||
482 | 0x00, 0xfc, | ||
483 | }, | ||
484 | }; | ||
485 | |||
486 | static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v96[GAMMA_LEVEL_NUM] = { | ||
487 | { | ||
488 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
489 | 0xdf, 0x1f, 0xd7, 0xdc, 0xb7, 0xe1, 0xc0, 0xaf, | ||
490 | 0xc4, 0xd2, 0xd0, 0xcf, 0x00, 0x4d, 0x00, 0x40, | ||
491 | 0x00, 0x5f, | ||
492 | }, { | ||
493 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
494 | 0xd5, 0x35, 0xcf, 0xdc, 0xc1, 0xe1, 0xbf, 0xb3, | ||
495 | 0xc1, 0xd2, 0xd1, 0xce, 0x00, 0x53, 0x00, 0x46, | ||
496 | 0x00, 0x67, | ||
497 | }, { | ||
498 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
499 | 0xd2, 0x64, 0xcf, 0xdb, 0xc6, 0xe1, 0xbd, 0xb3, | ||
500 | 0xbd, 0xd2, 0xd2, 0xce, 0x00, 0x59, 0x00, 0x4b, | ||
501 | 0x00, 0x6e, | ||
502 | }, { | ||
503 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
504 | 0xd0, 0x7c, 0xcf, 0xdb, 0xc9, 0xe0, 0xbc, 0xb4, | ||
505 | 0xbb, 0xcf, 0xd1, 0xcc, 0x00, 0x5f, 0x00, 0x50, | ||
506 | 0x00, 0x75, | ||
507 | }, { | ||
508 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
509 | 0xd0, 0x8e, 0xd1, 0xdb, 0xcc, 0xdf, 0xbb, 0xb6, | ||
510 | 0xb9, 0xd0, 0xd1, 0xcd, 0x00, 0x63, 0x00, 0x54, | ||
511 | 0x00, 0x7a, | ||
512 | }, { | ||
513 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
514 | 0xd1, 0x9e, 0xd5, 0xda, 0xcd, 0xdd, 0xbb, 0xb7, | ||
515 | 0xb9, 0xce, 0xce, 0xc9, 0x00, 0x68, 0x00, 0x59, | ||
516 | 0x00, 0x81, | ||
517 | }, { | ||
518 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff, | ||
519 | 0xd0, 0xa5, 0xd6, 0xda, 0xcf, 0xdd, 0xbb, 0xb7, | ||
520 | 0xb8, 0xcc, 0xcd, 0xc7, 0x00, 0x6c, 0x00, 0x5c, | ||
521 | 0x00, 0x86, | ||
522 | }, { | ||
523 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xfe, | ||
524 | 0xd0, 0xae, 0xd7, 0xd9, 0xd0, 0xdb, 0xb9, 0xb6, | ||
525 | 0xb5, 0xca, 0xcc, 0xc5, 0x00, 0x74, 0x00, 0x63, | ||
526 | 0x00, 0x90, | ||
527 | }, { | ||
528 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf9, | ||
529 | 0xcf, 0xb0, 0xd6, 0xd9, 0xd1, 0xdb, 0xb9, 0xb6, | ||
530 | 0xb4, 0xca, 0xcb, 0xc5, 0x00, 0x77, 0x00, 0x66, | ||
531 | 0x00, 0x94, | ||
532 | }, { | ||
533 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf7, | ||
534 | 0xcf, 0xb3, 0xd7, 0xd8, 0xd1, 0xd9, 0xb7, 0xb6, | ||
535 | 0xb3, 0xc9, 0xca, 0xc3, 0x00, 0x7b, 0x00, 0x69, | ||
536 | 0x00, 0x99, | ||
537 | |||
538 | }, { | ||
539 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfd, 0x2f, 0xf7, | ||
540 | 0xdf, 0xb5, 0xd6, 0xd8, 0xd1, 0xd8, 0xb6, 0xb5, | ||
541 | 0xb2, 0xca, 0xcb, 0xc4, 0x00, 0x7e, 0x00, 0x6c, | ||
542 | 0x00, 0x9d, | ||
543 | }, { | ||
544 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfa, 0x2f, 0xf5, | ||
545 | 0xce, 0xb6, 0xd5, 0xd7, 0xd2, 0xd8, 0xb6, 0xb4, | ||
546 | 0xb0, 0xc7, 0xc9, 0xc1, 0x00, 0x84, 0x00, 0x71, | ||
547 | 0x00, 0xa5, | ||
548 | }, { | ||
549 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf7, 0x2f, 0xf2, | ||
550 | 0xce, 0xb9, 0xd5, 0xd8, 0xd2, 0xd8, 0xb4, 0xb4, | ||
551 | 0xaf, 0xc7, 0xc9, 0xc1, 0x00, 0x87, 0x00, 0x73, | ||
552 | 0x00, 0xa8, | ||
553 | }, { | ||
554 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf5, 0x2f, 0xf0, | ||
555 | 0xdf, 0xba, 0xd5, 0xd7, 0xd2, 0xd7, 0xb4, 0xb4, | ||
556 | 0xaf, 0xc5, 0xc7, 0xbf, 0x00, 0x8a, 0x00, 0x76, | ||
557 | 0x00, 0xac, | ||
558 | }, { | ||
559 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf2, 0x2f, 0xed, | ||
560 | 0xcE, 0xbb, 0xd4, 0xd6, 0xd2, 0xd6, 0xb5, 0xb4, | ||
561 | 0xaF, 0xc5, 0xc7, 0xbf, 0x00, 0x8c, 0x00, 0x78, | ||
562 | 0x00, 0xaf, | ||
563 | }, { | ||
564 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x2f, 0xeb, | ||
565 | 0xcd, 0xbb, 0xd2, 0xd7, 0xd3, 0xd6, 0xb3, 0xb4, | ||
566 | 0xae, 0xc5, 0xc6, 0xbe, 0x00, 0x91, 0x00, 0x7d, | ||
567 | 0x00, 0xb6, | ||
568 | }, { | ||
569 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xee, 0x2f, 0xea, | ||
570 | 0xce, 0xbd, 0xd4, 0xd6, 0xd2, 0xd5, 0xb2, 0xb3, | ||
571 | 0xad, 0xc3, 0xc4, 0xbb, 0x00, 0x94, 0x00, 0x7f, | ||
572 | 0x00, 0xba, | ||
573 | }, { | ||
574 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xec, 0x2f, 0xe8, | ||
575 | 0xce, 0xbe, 0xd3, 0xd6, 0xd3, 0xd5, 0xb2, 0xb2, | ||
576 | 0xac, 0xc3, 0xc5, 0xbc, 0x00, 0x96, 0x00, 0x81, | ||
577 | 0x00, 0xbd, | ||
578 | }, { | ||
579 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xeb, 0x2f, 0xe7, | ||
580 | 0xce, 0xbf, 0xd3, 0xd6, 0xd2, 0xd5, 0xb1, 0xb2, | ||
581 | 0xab, 0xc2, 0xc4, 0xbb, 0x00, 0x99, 0x00, 0x83, | ||
582 | 0x00, 0xc0, | ||
583 | }, { | ||
584 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x5f, 0xe9, | ||
585 | 0xca, 0xbf, 0xd3, 0xd5, 0xd2, 0xd4, 0xb2, 0xb2, | ||
586 | 0xab, 0xc1, 0xc4, 0xba, 0x00, 0x9b, 0x00, 0x85, | ||
587 | 0x00, 0xc3, | ||
588 | }, { | ||
589 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xea, 0x5f, 0xe8, | ||
590 | 0xee, 0xbf, 0xd2, 0xd5, 0xd2, 0xd4, 0xb1, 0xb2, | ||
591 | 0xab, 0xc1, 0xc2, 0xb9, 0x00, 0x9D, 0x00, 0x87, | ||
592 | 0x00, 0xc6, | ||
593 | }, { | ||
594 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe9, 0x5f, 0xe7, | ||
595 | 0xcd, 0xbf, 0xd2, 0xd6, 0xd2, 0xd4, 0xb1, 0xb2, | ||
596 | 0xab, 0xbe, 0xc0, 0xb7, 0x00, 0xa1, 0x00, 0x8a, | ||
597 | 0x00, 0xca, | ||
598 | }, { | ||
599 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x61, 0xe6, | ||
600 | 0xcd, 0xbf, 0xd1, 0xd6, 0xd3, 0xd4, 0xaf, 0xb0, | ||
601 | 0xa9, 0xbe, 0xc1, 0xb7, 0x00, 0xa3, 0x00, 0x8b, | ||
602 | 0x00, 0xce, | ||
603 | }, { | ||
604 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x62, 0xe5, | ||
605 | 0xcc, 0xc0, 0xd0, 0xd6, 0xd2, 0xd4, 0xaf, 0xb1, | ||
606 | 0xa9, 0xbd, 0xc0, 0xb6, 0x00, 0xa5, 0x00, 0x8d, | ||
607 | 0x00, 0xd0, | ||
608 | }, { | ||
609 | 0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe7, 0x7f, 0xe3, | ||
610 | 0xcc, 0xc1, 0xd0, 0xd5, 0xd3, 0xd3, 0xae, 0xaf, | ||
611 | 0xa8, 0xbe, 0xc0, 0xb7, 0x00, 0xa8, 0x00, 0x90, | ||
612 | 0x00, 0xd3, | ||
613 | } | ||
614 | }; | ||
615 | |||
616 | static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v32[GAMMA_LEVEL_NUM] = { | ||
617 | { | ||
618 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0x72, 0x5e, 0x6b, | ||
619 | 0xa1, 0xa7, 0x9a, 0xb4, 0xcb, 0xb8, 0x92, 0xac, | ||
620 | 0x97, 0xb4, 0xc3, 0xb5, 0x00, 0x4e, 0x00, 0x37, | ||
621 | 0x00, 0x58, | ||
622 | }, { | ||
623 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0x85, 0x71, 0x7d, | ||
624 | 0xa6, 0xb6, 0xa1, 0xb5, 0xca, 0xba, 0x93, 0xac, | ||
625 | 0x98, 0xb2, 0xc0, 0xaf, 0x00, 0x59, 0x00, 0x43, | ||
626 | 0x00, 0x64, | ||
627 | }, { | ||
628 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa4, 0x94, 0x9e, | ||
629 | 0xa0, 0xbb, 0x9c, 0xc3, 0xd2, 0xc6, 0x93, 0xaa, | ||
630 | 0x95, 0xb7, 0xc2, 0xb4, 0x00, 0x65, 0x00, 0x50, | ||
631 | 0x00, 0x74, | ||
632 | }, { | ||
633 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa1, 0xa6, | ||
634 | 0xa0, 0xb9, 0x9b, 0xc3, 0xd1, 0xc8, 0x90, 0xa6, | ||
635 | 0x90, 0xbb, 0xc3, 0xb7, 0x00, 0x6f, 0x00, 0x5b, | ||
636 | 0x00, 0x80, | ||
637 | }, { | ||
638 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa6, 0x9d, 0x9f, | ||
639 | 0x9f, 0xb8, 0x9a, 0xc7, 0xd5, 0xcc, 0x90, 0xa5, | ||
640 | 0x8f, 0xb8, 0xc1, 0xb6, 0x00, 0x74, 0x00, 0x60, | ||
641 | 0x00, 0x85, | ||
642 | }, { | ||
643 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb3, 0xae, 0xae, | ||
644 | 0x9e, 0xb7, 0x9a, 0xc8, 0xd6, 0xce, 0x91, 0xa6, | ||
645 | 0x90, 0xb6, 0xc0, 0xb3, 0x00, 0x78, 0x00, 0x65, | ||
646 | 0x00, 0x8a, | ||
647 | }, { | ||
648 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa9, 0xa8, | ||
649 | 0xa3, 0xb9, 0x9e, 0xc4, 0xd3, 0xcb, 0x94, 0xa6, | ||
650 | 0x90, 0xb6, 0xbf, 0xb3, 0x00, 0x7c, 0x00, 0x69, | ||
651 | 0x00, 0x8e, | ||
652 | }, { | ||
653 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xaf, 0xaf, 0xa9, | ||
654 | 0xa5, 0xbc, 0xa2, 0xc7, 0xd5, 0xcd, 0x93, 0xa5, | ||
655 | 0x8f, 0xb4, 0xbd, 0xb1, 0x00, 0x83, 0x00, 0x70, | ||
656 | 0x00, 0x96, | ||
657 | }, { | ||
658 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xab, 0xa3, | ||
659 | 0xaa, 0xbf, 0xa7, 0xc5, 0xd3, 0xcb, 0x93, 0xa5, | ||
660 | 0x8f, 0xb2, 0xbb, 0xb0, 0x00, 0x86, 0x00, 0x74, | ||
661 | 0x00, 0x9b, | ||
662 | }, { | ||
663 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xb5, 0xab, | ||
664 | 0xab, 0xc0, 0xa9, 0xc7, 0xd4, 0xcc, 0x94, 0xa4, | ||
665 | 0x8f, 0xb1, 0xbb, 0xaf, 0x00, 0x8a, 0x00, 0x77, | ||
666 | 0x00, 0x9e, | ||
667 | }, { | ||
668 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb2, 0xa7, | ||
669 | 0xae, 0xc2, 0xab, 0xc5, 0xd3, 0xca, 0x93, 0xa4, | ||
670 | 0x8f, 0xb1, 0xba, 0xae, 0x00, 0x8d, 0x00, 0x7b, | ||
671 | 0x00, 0xa2, | ||
672 | }, { | ||
673 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xaf, 0xa3, | ||
674 | 0xb0, 0xc3, 0xae, 0xc4, 0xd1, 0xc8, 0x93, 0xa4, | ||
675 | 0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x8f, 0x00, 0x7d, | ||
676 | 0x00, 0xa5, | ||
677 | }, { | ||
678 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbd, 0xaf, | ||
679 | 0xae, 0xc1, 0xab, 0xc2, 0xd0, 0xc6, 0x94, 0xa4, | ||
680 | 0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x92, 0x00, 0x80, | ||
681 | 0x00, 0xa8, | ||
682 | }, { | ||
683 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xb9, 0xac, | ||
684 | 0xad, 0xc1, 0xab, 0xc4, 0xd1, 0xc7, 0x95, 0xa4, | ||
685 | 0x90, 0xb0, 0xb9, 0xad, 0x00, 0x95, 0x00, 0x84, | ||
686 | 0x00, 0xac, | ||
687 | }, { | ||
688 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb6, 0xa7, | ||
689 | 0xaf, 0xc2, 0xae, 0xc5, 0xd1, 0xc7, 0x93, 0xa3, | ||
690 | 0x8e, 0xb0, 0xb9, 0xad, 0x00, 0x98, 0x00, 0x86, | ||
691 | 0x00, 0xaf, | ||
692 | }, { | ||
693 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbf, 0xaf, | ||
694 | 0xad, 0xc1, 0xab, 0xc3, 0xd0, 0xc6, 0x94, 0xa3, | ||
695 | 0x8f, 0xaf, 0xb8, 0xac, 0x00, 0x9a, 0x00, 0x89, | ||
696 | 0x00, 0xb2, | ||
697 | }, { | ||
698 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xbc, 0xac, | ||
699 | 0xaf, 0xc2, 0xad, 0xc2, 0xcf, 0xc4, 0x94, 0xa3, | ||
700 | 0x90, 0xaf, 0xb8, 0xad, 0x00, 0x9c, 0x00, 0x8b, | ||
701 | 0x00, 0xb5, | ||
702 | }, { | ||
703 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7, | ||
704 | 0xb1, 0xc4, 0xaf, 0xc3, 0xcf, 0xc5, 0x94, 0xa3, | ||
705 | 0x8f, 0xae, 0xb7, 0xac, 0x00, 0x9f, 0x00, 0x8e, | ||
706 | 0x00, 0xb8, | ||
707 | }, { | ||
708 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7, | ||
709 | 0xaf, 0xc2, 0xad, 0xc1, 0xce, 0xc3, 0x95, 0xa3, | ||
710 | 0x90, 0xad, 0xb6, 0xab, 0x00, 0xa2, 0x00, 0x91, | ||
711 | 0x00, 0xbb, | ||
712 | }, { | ||
713 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xbe, 0xac, | ||
714 | 0xb1, 0xc4, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa4, | ||
715 | 0x91, 0xad, 0xb6, 0xab, 0x00, 0xa4, 0x00, 0x93, | ||
716 | 0x00, 0xbd, | ||
717 | }, { | ||
718 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8, | ||
719 | 0xb3, 0xc5, 0xb2, 0xc1, 0xcd, 0xc2, 0x95, 0xa3, | ||
720 | 0x90, 0xad, 0xb6, 0xab, 0x00, 0xa6, 0x00, 0x95, | ||
721 | 0x00, 0xc0, | ||
722 | }, { | ||
723 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8, | ||
724 | 0xb0, 0xc3, 0xaf, 0xc2, 0xce, 0xc2, 0x94, 0xa2, | ||
725 | 0x90, 0xac, 0xb6, 0xab, 0x00, 0xa8, 0x00, 0x98, | ||
726 | 0x00, 0xc3, | ||
727 | }, { | ||
728 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xb8, 0xa5, | ||
729 | 0xb3, 0xc5, 0xb2, 0xc1, 0xcc, 0xc0, 0x95, 0xa2, | ||
730 | 0x90, 0xad, 0xb6, 0xab, 0x00, 0xaa, 0x00, 0x9a, | ||
731 | 0x00, 0xc5, | ||
732 | }, { | ||
733 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xc0, 0xac, | ||
734 | 0xb0, 0xc3, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa2, | ||
735 | 0x90, 0xac, 0xb5, 0xa9, 0x00, 0xac, 0x00, 0x9c, | ||
736 | 0x00, 0xc8, | ||
737 | }, { | ||
738 | 0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbd, 0xa8, | ||
739 | 0xaf, 0xc2, 0xaf, 0xc1, 0xcc, 0xc0, 0x95, 0xa2, | ||
740 | 0x90, 0xac, 0xb5, 0xaa, 0x00, 0xb1, 0x00, 0xa1, | ||
741 | 0x00, 0xcc, | ||
742 | }, | ||
743 | }; | ||
744 | |||
745 | static const struct s6e8aa0_variant s6e8aa0_variants[] = { | ||
746 | { | ||
747 | .version = 32, | ||
748 | .gamma_tables = s6e8aa0_gamma_tables_v32, | ||
749 | }, { | ||
750 | .version = 96, | ||
751 | .gamma_tables = s6e8aa0_gamma_tables_v96, | ||
752 | }, { | ||
753 | .version = 142, | ||
754 | .gamma_tables = s6e8aa0_gamma_tables_v142, | ||
755 | }, { | ||
756 | .version = 210, | ||
757 | .gamma_tables = s6e8aa0_gamma_tables_v142, | ||
758 | } | ||
759 | }; | ||
760 | |||
761 | static void s6e8aa0_brightness_set(struct s6e8aa0 *ctx) | ||
762 | { | ||
763 | const u8 *gamma; | ||
764 | |||
765 | if (ctx->error) | ||
766 | return; | ||
767 | |||
768 | gamma = ctx->variant->gamma_tables[ctx->brightness]; | ||
769 | |||
770 | if (ctx->version >= 142) | ||
771 | s6e8aa0_elvss_nvm_set(ctx); | ||
772 | |||
773 | s6e8aa0_dcs_write(ctx, gamma, GAMMA_TABLE_LEN); | ||
774 | |||
775 | /* update gamma table. */ | ||
776 | s6e8aa0_dcs_write_seq_static(ctx, 0xf7, 0x03); | ||
777 | } | ||
778 | |||
779 | static void s6e8aa0_panel_init(struct s6e8aa0 *ctx) | ||
780 | { | ||
781 | s6e8aa0_apply_level_1_key(ctx); | ||
782 | s6e8aa0_apply_level_2_key(ctx); | ||
783 | msleep(20); | ||
784 | |||
785 | s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); | ||
786 | msleep(40); | ||
787 | |||
788 | s6e8aa0_panel_cond_set(ctx); | ||
789 | s6e8aa0_display_condition_set(ctx); | ||
790 | s6e8aa0_brightness_set(ctx); | ||
791 | s6e8aa0_etc_source_control(ctx); | ||
792 | s6e8aa0_etc_pentile_control(ctx); | ||
793 | s6e8aa0_elvss_nvm_set(ctx); | ||
794 | s6e8aa0_etc_power_control(ctx); | ||
795 | s6e8aa0_etc_elvss_control(ctx); | ||
796 | msleep(ctx->init_delay); | ||
797 | } | ||
798 | |||
799 | static void s6e8aa0_set_maximum_return_packet_size(struct s6e8aa0 *ctx, | ||
800 | int size) | ||
801 | { | ||
802 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); | ||
803 | const struct mipi_dsi_host_ops *ops = dsi->host->ops; | ||
804 | u8 buf[] = {size, 0}; | ||
805 | struct mipi_dsi_msg msg = { | ||
806 | .channel = dsi->channel, | ||
807 | .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, | ||
808 | .tx_len = sizeof(buf), | ||
809 | .tx_buf = buf | ||
810 | }; | ||
811 | int ret; | ||
812 | |||
813 | if (ctx->error < 0) | ||
814 | return; | ||
815 | |||
816 | if (!ops || !ops->transfer) | ||
817 | ret = -EIO; | ||
818 | else | ||
819 | ret = ops->transfer(dsi->host, &msg); | ||
820 | |||
821 | if (ret < 0) { | ||
822 | dev_err(ctx->dev, | ||
823 | "error %d setting maximum return packet size to %d\n", | ||
824 | ret, size); | ||
825 | ctx->error = ret; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx) | ||
830 | { | ||
831 | u8 id[3]; | ||
832 | int ret, i; | ||
833 | |||
834 | ret = s6e8aa0_dcs_read(ctx, 0xd1, id, ARRAY_SIZE(id)); | ||
835 | if (ret < ARRAY_SIZE(id) || id[0] == 0x00) { | ||
836 | dev_err(ctx->dev, "read id failed\n"); | ||
837 | ctx->error = -EIO; | ||
838 | return; | ||
839 | } | ||
840 | |||
841 | dev_info(ctx->dev, "ID: 0x%2x, 0x%2x, 0x%2x\n", id[0], id[1], id[2]); | ||
842 | |||
843 | for (i = 0; i < ARRAY_SIZE(s6e8aa0_variants); ++i) { | ||
844 | if (id[1] == s6e8aa0_variants[i].version) | ||
845 | break; | ||
846 | } | ||
847 | if (i >= ARRAY_SIZE(s6e8aa0_variants)) { | ||
848 | dev_err(ctx->dev, "unsupported display version %d\n", id[1]); | ||
849 | ctx->error = -EINVAL; | ||
850 | } | ||
851 | |||
852 | ctx->variant = &s6e8aa0_variants[i]; | ||
853 | ctx->version = id[1]; | ||
854 | ctx->id = id[2]; | ||
855 | } | ||
856 | |||
857 | static void s6e8aa0_set_sequence(struct s6e8aa0 *ctx) | ||
858 | { | ||
859 | s6e8aa0_set_maximum_return_packet_size(ctx, 3); | ||
860 | s6e8aa0_read_mtp_id(ctx); | ||
861 | s6e8aa0_panel_init(ctx); | ||
862 | s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); | ||
863 | } | ||
864 | |||
865 | static int s6e8aa0_power_on(struct s6e8aa0 *ctx) | ||
866 | { | ||
867 | int ret; | ||
868 | |||
869 | ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
870 | if (ret < 0) | ||
871 | return ret; | ||
872 | |||
873 | msleep(ctx->power_on_delay); | ||
874 | |||
875 | gpiod_set_value(ctx->reset_gpio, 0); | ||
876 | usleep_range(10000, 11000); | ||
877 | gpiod_set_value(ctx->reset_gpio, 1); | ||
878 | |||
879 | msleep(ctx->reset_delay); | ||
880 | |||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | static int s6e8aa0_power_off(struct s6e8aa0 *ctx) | ||
885 | { | ||
886 | return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); | ||
887 | } | ||
888 | |||
889 | static int s6e8aa0_disable(struct drm_panel *panel) | ||
890 | { | ||
891 | struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel); | ||
892 | |||
893 | s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); | ||
894 | s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); | ||
895 | msleep(40); | ||
896 | |||
897 | s6e8aa0_clear_error(ctx); | ||
898 | |||
899 | return s6e8aa0_power_off(ctx); | ||
900 | } | ||
901 | |||
902 | static int s6e8aa0_enable(struct drm_panel *panel) | ||
903 | { | ||
904 | struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel); | ||
905 | int ret; | ||
906 | |||
907 | ret = s6e8aa0_power_on(ctx); | ||
908 | if (ret < 0) | ||
909 | return ret; | ||
910 | |||
911 | s6e8aa0_set_sequence(ctx); | ||
912 | ret = ctx->error; | ||
913 | |||
914 | if (ret < 0) | ||
915 | s6e8aa0_disable(panel); | ||
916 | |||
917 | return ret; | ||
918 | } | ||
919 | |||
920 | static int s6e8aa0_get_modes(struct drm_panel *panel) | ||
921 | { | ||
922 | struct drm_connector *connector = panel->connector; | ||
923 | struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel); | ||
924 | struct drm_display_mode *mode; | ||
925 | |||
926 | mode = drm_mode_create(connector->dev); | ||
927 | if (!mode) { | ||
928 | DRM_ERROR("failed to create a new display mode\n"); | ||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | drm_display_mode_from_videomode(&ctx->vm, mode); | ||
933 | mode->width_mm = ctx->width_mm; | ||
934 | mode->height_mm = ctx->height_mm; | ||
935 | connector->display_info.width_mm = mode->width_mm; | ||
936 | connector->display_info.height_mm = mode->height_mm; | ||
937 | |||
938 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
939 | drm_mode_probed_add(connector, mode); | ||
940 | |||
941 | return 1; | ||
942 | } | ||
943 | |||
944 | static const struct drm_panel_funcs s6e8aa0_drm_funcs = { | ||
945 | .disable = s6e8aa0_disable, | ||
946 | .enable = s6e8aa0_enable, | ||
947 | .get_modes = s6e8aa0_get_modes, | ||
948 | }; | ||
949 | |||
950 | static int s6e8aa0_parse_dt(struct s6e8aa0 *ctx) | ||
951 | { | ||
952 | struct device *dev = ctx->dev; | ||
953 | struct device_node *np = dev->of_node; | ||
954 | int ret; | ||
955 | |||
956 | ret = of_get_videomode(np, &ctx->vm, 0); | ||
957 | if (ret < 0) | ||
958 | return ret; | ||
959 | |||
960 | of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay); | ||
961 | of_property_read_u32(np, "reset-delay", &ctx->reset_delay); | ||
962 | of_property_read_u32(np, "init-delay", &ctx->init_delay); | ||
963 | of_property_read_u32(np, "panel-width-mm", &ctx->width_mm); | ||
964 | of_property_read_u32(np, "panel-height-mm", &ctx->height_mm); | ||
965 | |||
966 | ctx->flip_horizontal = of_property_read_bool(np, "flip-horizontal"); | ||
967 | ctx->flip_vertical = of_property_read_bool(np, "flip-vertical"); | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int s6e8aa0_probe(struct mipi_dsi_device *dsi) | ||
973 | { | ||
974 | struct device *dev = &dsi->dev; | ||
975 | struct s6e8aa0 *ctx; | ||
976 | int ret; | ||
977 | |||
978 | ctx = devm_kzalloc(dev, sizeof(struct s6e8aa0), GFP_KERNEL); | ||
979 | if (!ctx) | ||
980 | return -ENOMEM; | ||
981 | |||
982 | mipi_dsi_set_drvdata(dsi, ctx); | ||
983 | |||
984 | ctx->dev = dev; | ||
985 | |||
986 | dsi->lanes = 4; | ||
987 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
988 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | ||
989 | | MIPI_DSI_MODE_VIDEO_HFP | MIPI_DSI_MODE_VIDEO_HBP | ||
990 | | MIPI_DSI_MODE_VIDEO_HSA | MIPI_DSI_MODE_EOT_PACKET | ||
991 | | MIPI_DSI_MODE_VSYNC_FLUSH | MIPI_DSI_MODE_VIDEO_AUTO_VERT; | ||
992 | |||
993 | ret = s6e8aa0_parse_dt(ctx); | ||
994 | if (ret < 0) | ||
995 | return ret; | ||
996 | |||
997 | ctx->supplies[0].supply = "vdd3"; | ||
998 | ctx->supplies[1].supply = "vci"; | ||
999 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), | ||
1000 | ctx->supplies); | ||
1001 | if (ret < 0) { | ||
1002 | dev_err(dev, "failed to get regulators: %d\n", ret); | ||
1003 | return ret; | ||
1004 | } | ||
1005 | |||
1006 | ctx->reset_gpio = devm_gpiod_get(dev, "reset"); | ||
1007 | if (IS_ERR(ctx->reset_gpio)) { | ||
1008 | dev_err(dev, "cannot get reset-gpios %ld\n", | ||
1009 | PTR_ERR(ctx->reset_gpio)); | ||
1010 | return PTR_ERR(ctx->reset_gpio); | ||
1011 | } | ||
1012 | ret = gpiod_direction_output(ctx->reset_gpio, 1); | ||
1013 | if (ret < 0) { | ||
1014 | dev_err(dev, "cannot configure reset-gpios %d\n", ret); | ||
1015 | return ret; | ||
1016 | } | ||
1017 | |||
1018 | ctx->brightness = GAMMA_LEVEL_NUM - 1; | ||
1019 | |||
1020 | drm_panel_init(&ctx->panel); | ||
1021 | ctx->panel.dev = dev; | ||
1022 | ctx->panel.funcs = &s6e8aa0_drm_funcs; | ||
1023 | |||
1024 | ret = drm_panel_add(&ctx->panel); | ||
1025 | if (ret < 0) | ||
1026 | return ret; | ||
1027 | |||
1028 | ret = mipi_dsi_attach(dsi); | ||
1029 | if (ret < 0) | ||
1030 | drm_panel_remove(&ctx->panel); | ||
1031 | |||
1032 | return ret; | ||
1033 | } | ||
1034 | |||
1035 | static int s6e8aa0_remove(struct mipi_dsi_device *dsi) | ||
1036 | { | ||
1037 | struct s6e8aa0 *ctx = mipi_dsi_get_drvdata(dsi); | ||
1038 | |||
1039 | mipi_dsi_detach(dsi); | ||
1040 | drm_panel_remove(&ctx->panel); | ||
1041 | |||
1042 | return 0; | ||
1043 | } | ||
1044 | |||
1045 | static struct of_device_id s6e8aa0_of_match[] = { | ||
1046 | { .compatible = "samsung,s6e8aa0" }, | ||
1047 | { } | ||
1048 | }; | ||
1049 | MODULE_DEVICE_TABLE(of, s6e8aa0_of_match); | ||
1050 | |||
1051 | static struct mipi_dsi_driver s6e8aa0_driver = { | ||
1052 | .probe = s6e8aa0_probe, | ||
1053 | .remove = s6e8aa0_remove, | ||
1054 | .driver = { | ||
1055 | .name = "panel_s6e8aa0", | ||
1056 | .owner = THIS_MODULE, | ||
1057 | .of_match_table = s6e8aa0_of_match, | ||
1058 | }, | ||
1059 | }; | ||
1060 | module_mipi_dsi_driver(s6e8aa0_driver); | ||
1061 | |||
1062 | MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); | ||
1063 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
1064 | MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>"); | ||
1065 | MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>"); | ||
1066 | MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>"); | ||
1067 | MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>"); | ||
1068 | MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver"); | ||
1069 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index d32628acdd90..7209df15a3cd 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h | |||
@@ -17,6 +17,11 @@ | |||
17 | struct mipi_dsi_host; | 17 | struct mipi_dsi_host; |
18 | struct mipi_dsi_device; | 18 | struct mipi_dsi_device; |
19 | 19 | ||
20 | /* request ACK from peripheral */ | ||
21 | #define MIPI_DSI_MSG_REQ_ACK BIT(0) | ||
22 | /* use Low Power Mode to transmit message */ | ||
23 | #define MIPI_DSI_MSG_USE_LPM BIT(1) | ||
24 | |||
20 | /** | 25 | /** |
21 | * struct mipi_dsi_msg - read/write DSI buffer | 26 | * struct mipi_dsi_msg - read/write DSI buffer |
22 | * @channel: virtual channel id | 27 | * @channel: virtual channel id |
@@ -29,6 +34,7 @@ struct mipi_dsi_device; | |||
29 | struct mipi_dsi_msg { | 34 | struct mipi_dsi_msg { |
30 | u8 channel; | 35 | u8 channel; |
31 | u8 type; | 36 | u8 type; |
37 | u16 flags; | ||
32 | 38 | ||
33 | size_t tx_len; | 39 | size_t tx_len; |
34 | const void *tx_buf; | 40 | const void *tx_buf; |