diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2013-06-17 07:48:27 -0400 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2013-08-09 17:17:53 -0400 |
commit | 90374b5c25c9f04895c52a1e7a2468ee8dac525b (patch) | |
tree | 12d6f168083bdab0902c003b068527b3a7c130fe /drivers | |
parent | 7cbc05cb518304b746bea00bc7c0b005217bcaf7 (diff) |
drm/rcar-du: Add internal LVDS encoder support
The R8A7790 includes two internal LVDS encoders. Support them in the DU
driver.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/rcar-du/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 38 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 196 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 69 |
12 files changed, 374 insertions, 3 deletions
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 72887df8dd76..c590cd9dca0b 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig | |||
@@ -7,3 +7,10 @@ config DRM_RCAR_DU | |||
7 | help | 7 | help |
8 | Choose this option if you have an R-Car chipset. | 8 | Choose this option if you have an R-Car chipset. |
9 | If M is selected the module will be called rcar-du-drm. | 9 | If M is selected the module will be called rcar-du-drm. |
10 | |||
11 | config DRM_RCAR_LVDS | ||
12 | bool "R-Car DU LVDS Encoder Support" | ||
13 | depends on DRM_RCAR_DU | ||
14 | help | ||
15 | Enable support the R-Car Display Unit embedded LVDS encoders | ||
16 | (currently only on R8A7790). | ||
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index b9b5e666fbba..12b8d4477835 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile | |||
@@ -7,4 +7,6 @@ rcar-du-drm-y := rcar_du_crtc.o \ | |||
7 | rcar_du_plane.o \ | 7 | rcar_du_plane.o \ |
8 | rcar_du_vgacon.o | 8 | rcar_du_vgacon.o |
9 | 9 | ||
10 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o | 10 | rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o |
11 | |||
12 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 245800ddd1a8..33df7a583143 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c | |||
@@ -26,8 +26,6 @@ | |||
26 | #include "rcar_du_plane.h" | 26 | #include "rcar_du_plane.h" |
27 | #include "rcar_du_regs.h" | 27 | #include "rcar_du_regs.h" |
28 | 28 | ||
29 | #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) | ||
30 | |||
31 | static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) | 29 | static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) |
32 | { | 30 | { |
33 | struct rcar_du_device *rcdu = rcrtc->group->dev; | 31 | struct rcar_du_device *rcdu = rcrtc->group->dev; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 39a983d13afb..43e7575c700c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h | |||
@@ -39,6 +39,8 @@ struct rcar_du_crtc { | |||
39 | struct rcar_du_plane *plane; | 39 | struct rcar_du_plane *plane; |
40 | }; | 40 | }; |
41 | 41 | ||
42 | #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) | ||
43 | |||
42 | int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); | 44 | int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); |
43 | void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); | 45 | void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); |
44 | void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, | 46 | void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 4bc399734490..38a8b52624ce 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c | |||
@@ -232,6 +232,7 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { | |||
232 | .encoder_type = DRM_MODE_ENCODER_NONE, | 232 | .encoder_type = DRM_MODE_ENCODER_NONE, |
233 | }, | 233 | }, |
234 | }, | 234 | }, |
235 | .num_lvds = 0, | ||
235 | }; | 236 | }; |
236 | 237 | ||
237 | static const struct rcar_du_device_info rcar_du_r8a7790_info = { | 238 | static const struct rcar_du_device_info rcar_du_r8a7790_info = { |
@@ -255,6 +256,7 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { | |||
255 | .encoder_type = DRM_MODE_ENCODER_LVDS, | 256 | .encoder_type = DRM_MODE_ENCODER_LVDS, |
256 | }, | 257 | }, |
257 | }, | 258 | }, |
259 | .num_lvds = 2, | ||
258 | }; | 260 | }; |
259 | 261 | ||
260 | static const struct platform_device_id rcar_du_id_table[] = { | 262 | static const struct platform_device_id rcar_du_id_table[] = { |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 924f5e08f060..050d71c1f785 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h | |||
@@ -24,6 +24,7 @@ struct clk; | |||
24 | struct device; | 24 | struct device; |
25 | struct drm_device; | 25 | struct drm_device; |
26 | struct rcar_du_device; | 26 | struct rcar_du_device; |
27 | struct rcar_du_lvdsenc; | ||
27 | 28 | ||
28 | #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ | 29 | #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ |
29 | #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ | 30 | #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ |
@@ -48,11 +49,13 @@ struct rcar_du_output_routing { | |||
48 | * @features: device features (RCAR_DU_FEATURE_*) | 49 | * @features: device features (RCAR_DU_FEATURE_*) |
49 | * @num_crtcs: total number of CRTCs | 50 | * @num_crtcs: total number of CRTCs |
50 | * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) | 51 | * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) |
52 | * @num_lvds: number of internal LVDS encoders | ||
51 | */ | 53 | */ |
52 | struct rcar_du_device_info { | 54 | struct rcar_du_device_info { |
53 | unsigned int features; | 55 | unsigned int features; |
54 | unsigned int num_crtcs; | 56 | unsigned int num_crtcs; |
55 | struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; | 57 | struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; |
58 | unsigned int num_lvds; | ||
56 | }; | 59 | }; |
57 | 60 | ||
58 | struct rcar_du_device { | 61 | struct rcar_du_device { |
@@ -70,6 +73,7 @@ struct rcar_du_device { | |||
70 | struct rcar_du_group groups[2]; | 73 | struct rcar_du_group groups[2]; |
71 | 74 | ||
72 | unsigned int dpad0_source; | 75 | unsigned int dpad0_source; |
76 | struct rcar_du_lvdsenc *lvds[2]; | ||
73 | }; | 77 | }; |
74 | 78 | ||
75 | static inline bool rcar_du_has(struct rcar_du_device *rcdu, | 79 | static inline bool rcar_du_has(struct rcar_du_device *rcdu, |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 2aac28d21f87..3daa7a168dc6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c | |||
@@ -11,6 +11,8 @@ | |||
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/export.h> | ||
15 | |||
14 | #include <drm/drmP.h> | 16 | #include <drm/drmP.h> |
15 | #include <drm/drm_crtc.h> | 17 | #include <drm/drm_crtc.h> |
16 | #include <drm/drm_crtc_helper.h> | 18 | #include <drm/drm_crtc_helper.h> |
@@ -19,6 +21,7 @@ | |||
19 | #include "rcar_du_encoder.h" | 21 | #include "rcar_du_encoder.h" |
20 | #include "rcar_du_kms.h" | 22 | #include "rcar_du_kms.h" |
21 | #include "rcar_du_lvdscon.h" | 23 | #include "rcar_du_lvdscon.h" |
24 | #include "rcar_du_lvdsenc.h" | ||
22 | #include "rcar_du_vgacon.h" | 25 | #include "rcar_du_vgacon.h" |
23 | 26 | ||
24 | /* ----------------------------------------------------------------------------- | 27 | /* ----------------------------------------------------------------------------- |
@@ -39,12 +42,17 @@ rcar_du_connector_best_encoder(struct drm_connector *connector) | |||
39 | 42 | ||
40 | static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) | 43 | static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) |
41 | { | 44 | { |
45 | struct rcar_du_encoder *renc = to_rcar_encoder(encoder); | ||
46 | |||
47 | if (renc->lvds) | ||
48 | rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); | ||
42 | } | 49 | } |
43 | 50 | ||
44 | static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, | 51 | static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, |
45 | const struct drm_display_mode *mode, | 52 | const struct drm_display_mode *mode, |
46 | struct drm_display_mode *adjusted_mode) | 53 | struct drm_display_mode *adjusted_mode) |
47 | { | 54 | { |
55 | struct rcar_du_encoder *renc = to_rcar_encoder(encoder); | ||
48 | const struct drm_display_mode *panel_mode; | 56 | const struct drm_display_mode *panel_mode; |
49 | struct drm_device *dev = encoder->dev; | 57 | struct drm_device *dev = encoder->dev; |
50 | struct drm_connector *connector; | 58 | struct drm_connector *connector; |
@@ -82,15 +90,32 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, | |||
82 | /* The flat panel mode is fixed, just copy it to the adjusted mode. */ | 90 | /* The flat panel mode is fixed, just copy it to the adjusted mode. */ |
83 | drm_mode_copy(adjusted_mode, panel_mode); | 91 | drm_mode_copy(adjusted_mode, panel_mode); |
84 | 92 | ||
93 | /* The internal LVDS encoder has a clock frequency operating range of | ||
94 | * 30MHz to 150MHz. Clamp the clock accordingly. | ||
95 | */ | ||
96 | if (renc->lvds) | ||
97 | adjusted_mode->clock = clamp(adjusted_mode->clock, | ||
98 | 30000, 150000); | ||
99 | |||
85 | return true; | 100 | return true; |
86 | } | 101 | } |
87 | 102 | ||
88 | static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) | 103 | static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) |
89 | { | 104 | { |
105 | struct rcar_du_encoder *renc = to_rcar_encoder(encoder); | ||
106 | |||
107 | if (renc->lvds) | ||
108 | rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, | ||
109 | DRM_MODE_DPMS_OFF); | ||
90 | } | 110 | } |
91 | 111 | ||
92 | static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) | 112 | static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) |
93 | { | 113 | { |
114 | struct rcar_du_encoder *renc = to_rcar_encoder(encoder); | ||
115 | |||
116 | if (renc->lvds) | ||
117 | rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, | ||
118 | DRM_MODE_DPMS_ON); | ||
94 | } | 119 | } |
95 | 120 | ||
96 | static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, | 121 | static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, |
@@ -129,6 +154,19 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, | |||
129 | 154 | ||
130 | renc->output = output; | 155 | renc->output = output; |
131 | 156 | ||
157 | switch (output) { | ||
158 | case RCAR_DU_OUTPUT_LVDS0: | ||
159 | renc->lvds = rcdu->lvds[0]; | ||
160 | break; | ||
161 | |||
162 | case RCAR_DU_OUTPUT_LVDS1: | ||
163 | renc->lvds = rcdu->lvds[1]; | ||
164 | break; | ||
165 | |||
166 | default: | ||
167 | break; | ||
168 | } | ||
169 | |||
132 | switch (type) { | 170 | switch (type) { |
133 | case RCAR_DU_ENCODER_VGA: | 171 | case RCAR_DU_ENCODER_VGA: |
134 | encoder_type = DRM_MODE_ENCODER_DAC; | 172 | encoder_type = DRM_MODE_ENCODER_DAC; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 2310416ea21f..0e5a65e45d0e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h | |||
@@ -19,10 +19,12 @@ | |||
19 | #include <drm/drm_crtc.h> | 19 | #include <drm/drm_crtc.h> |
20 | 20 | ||
21 | struct rcar_du_device; | 21 | struct rcar_du_device; |
22 | struct rcar_du_lvdsenc; | ||
22 | 23 | ||
23 | struct rcar_du_encoder { | 24 | struct rcar_du_encoder { |
24 | struct drm_encoder encoder; | 25 | struct drm_encoder encoder; |
25 | enum rcar_du_output output; | 26 | enum rcar_du_output output; |
27 | struct rcar_du_lvdsenc *lvds; | ||
26 | }; | 28 | }; |
27 | 29 | ||
28 | #define to_rcar_encoder(e) \ | 30 | #define to_rcar_encoder(e) \ |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 2b92e68a09f0..cc71b1a0c3ce 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "rcar_du_drv.h" | 21 | #include "rcar_du_drv.h" |
22 | #include "rcar_du_encoder.h" | 22 | #include "rcar_du_encoder.h" |
23 | #include "rcar_du_kms.h" | 23 | #include "rcar_du_kms.h" |
24 | #include "rcar_du_lvdsenc.h" | ||
24 | #include "rcar_du_regs.h" | 25 | #include "rcar_du_regs.h" |
25 | 26 | ||
26 | /* ----------------------------------------------------------------------------- | 27 | /* ----------------------------------------------------------------------------- |
@@ -217,6 +218,10 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) | |||
217 | } | 218 | } |
218 | 219 | ||
219 | /* Initialize the encoders. */ | 220 | /* Initialize the encoders. */ |
221 | ret = rcar_du_lvdsenc_init(rcdu); | ||
222 | if (ret < 0) | ||
223 | return ret; | ||
224 | |||
220 | for (i = 0; i < rcdu->pdata->num_encoders; ++i) { | 225 | for (i = 0; i < rcdu->pdata->num_encoders; ++i) { |
221 | const struct rcar_du_encoder_data *pdata = | 226 | const struct rcar_du_encoder_data *pdata = |
222 | &rcdu->pdata->encoders[i]; | 227 | &rcdu->pdata->encoders[i]; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c new file mode 100644 index 000000000000..a0f6a1781925 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Corporation | ||
5 | * | ||
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/slab.h> | ||
19 | |||
20 | #include "rcar_du_drv.h" | ||
21 | #include "rcar_du_encoder.h" | ||
22 | #include "rcar_du_lvdsenc.h" | ||
23 | #include "rcar_lvds_regs.h" | ||
24 | |||
25 | struct rcar_du_lvdsenc { | ||
26 | struct rcar_du_device *dev; | ||
27 | |||
28 | unsigned int index; | ||
29 | void __iomem *mmio; | ||
30 | struct clk *clock; | ||
31 | int dpms; | ||
32 | |||
33 | enum rcar_lvds_input input; | ||
34 | }; | ||
35 | |||
36 | static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) | ||
37 | { | ||
38 | iowrite32(data, lvds->mmio + reg); | ||
39 | } | ||
40 | |||
41 | static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, | ||
42 | struct rcar_du_crtc *rcrtc) | ||
43 | { | ||
44 | const struct drm_display_mode *mode = &rcrtc->crtc.mode; | ||
45 | unsigned int freq = mode->clock; | ||
46 | u32 lvdcr0; | ||
47 | u32 pllcr; | ||
48 | int ret; | ||
49 | |||
50 | if (lvds->dpms == DRM_MODE_DPMS_ON) | ||
51 | return 0; | ||
52 | |||
53 | ret = clk_prepare_enable(lvds->clock); | ||
54 | if (ret < 0) | ||
55 | return ret; | ||
56 | |||
57 | /* PLL clock configuration */ | ||
58 | if (freq <= 38000) | ||
59 | pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; | ||
60 | else if (freq <= 60000) | ||
61 | pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; | ||
62 | else if (freq <= 121000) | ||
63 | pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | ||
64 | else | ||
65 | pllcr = LVDPLLCR_PLLDLYCNT_150M; | ||
66 | |||
67 | rcar_lvds_write(lvds, LVDPLLCR, pllcr); | ||
68 | |||
69 | /* Hardcode the channels and control signals routing for now. | ||
70 | * | ||
71 | * HSYNC -> CTRL0 | ||
72 | * VSYNC -> CTRL1 | ||
73 | * DISP -> CTRL2 | ||
74 | * 0 -> CTRL3 | ||
75 | * | ||
76 | * Channels 1 and 3 are switched on ES1. | ||
77 | */ | ||
78 | rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | | ||
79 | LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | | ||
80 | LVDCTRCR_CTR0SEL_HSYNC); | ||
81 | rcar_lvds_write(lvds, LVDCHCR, | ||
82 | LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | | ||
83 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1)); | ||
84 | |||
85 | /* Select the input, hardcode mode 0, enable LVDS operation and turn | ||
86 | * bias circuitry on. | ||
87 | */ | ||
88 | lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; | ||
89 | if (rcrtc->index == 2) | ||
90 | lvdcr0 |= LVDCR0_DUSEL; | ||
91 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | ||
92 | |||
93 | /* Turn all the channels on. */ | ||
94 | rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | | ||
95 | LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); | ||
96 | |||
97 | /* Turn the PLL on, wait for the startup delay, and turn the output | ||
98 | * on. | ||
99 | */ | ||
100 | lvdcr0 |= LVDCR0_PLLEN; | ||
101 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | ||
102 | |||
103 | usleep_range(100, 150); | ||
104 | |||
105 | lvdcr0 |= LVDCR0_LVRES; | ||
106 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | ||
107 | |||
108 | lvds->dpms = DRM_MODE_DPMS_ON; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) | ||
113 | { | ||
114 | if (lvds->dpms == DRM_MODE_DPMS_OFF) | ||
115 | return; | ||
116 | |||
117 | rcar_lvds_write(lvds, LVDCR0, 0); | ||
118 | rcar_lvds_write(lvds, LVDCR1, 0); | ||
119 | |||
120 | clk_disable_unprepare(lvds->clock); | ||
121 | |||
122 | lvds->dpms = DRM_MODE_DPMS_OFF; | ||
123 | } | ||
124 | |||
125 | int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, | ||
126 | struct drm_crtc *crtc, int mode) | ||
127 | { | ||
128 | if (mode == DRM_MODE_DPMS_OFF) { | ||
129 | rcar_du_lvdsenc_stop(lvds); | ||
130 | return 0; | ||
131 | } else if (crtc) { | ||
132 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | ||
133 | return rcar_du_lvdsenc_start(lvds, rcrtc); | ||
134 | } else | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, | ||
139 | struct platform_device *pdev) | ||
140 | { | ||
141 | struct resource *mem; | ||
142 | char name[7]; | ||
143 | |||
144 | sprintf(name, "lvds.%u", lvds->index); | ||
145 | |||
146 | mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | ||
147 | if (mem == NULL) { | ||
148 | dev_err(&pdev->dev, "failed to get memory resource for %s\n", | ||
149 | name); | ||
150 | return -EINVAL; | ||
151 | } | ||
152 | |||
153 | lvds->mmio = devm_ioremap_resource(&pdev->dev, mem); | ||
154 | if (lvds->mmio == NULL) { | ||
155 | dev_err(&pdev->dev, "failed to remap memory resource for %s\n", | ||
156 | name); | ||
157 | return -ENOMEM; | ||
158 | } | ||
159 | |||
160 | lvds->clock = devm_clk_get(&pdev->dev, name); | ||
161 | if (IS_ERR(lvds->clock)) { | ||
162 | dev_err(&pdev->dev, "failed to get clock for %s\n", name); | ||
163 | return PTR_ERR(lvds->clock); | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) | ||
170 | { | ||
171 | struct platform_device *pdev = to_platform_device(rcdu->dev); | ||
172 | struct rcar_du_lvdsenc *lvds; | ||
173 | unsigned int i; | ||
174 | int ret; | ||
175 | |||
176 | for (i = 0; i < rcdu->info->num_lvds; ++i) { | ||
177 | lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); | ||
178 | if (lvds == NULL) { | ||
179 | dev_err(&pdev->dev, "failed to allocate private data\n"); | ||
180 | return -ENOMEM; | ||
181 | } | ||
182 | |||
183 | lvds->dev = rcdu; | ||
184 | lvds->index = i; | ||
185 | lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; | ||
186 | lvds->dpms = DRM_MODE_DPMS_OFF; | ||
187 | |||
188 | ret = rcar_du_lvdsenc_get_resources(lvds, pdev); | ||
189 | if (ret < 0) | ||
190 | return ret; | ||
191 | |||
192 | rcdu->lvds[i] = lvds; | ||
193 | } | ||
194 | |||
195 | return 0; | ||
196 | } | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h new file mode 100644 index 000000000000..7051c6de19ae --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Corporation | ||
5 | * | ||
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #ifndef __RCAR_DU_LVDSENC_H__ | ||
15 | #define __RCAR_DU_LVDSENC_H__ | ||
16 | |||
17 | #include <linux/io.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_data/rcar-du.h> | ||
20 | |||
21 | struct rcar_drm_crtc; | ||
22 | struct rcar_du_lvdsenc; | ||
23 | |||
24 | enum rcar_lvds_input { | ||
25 | RCAR_LVDS_INPUT_DU0, | ||
26 | RCAR_LVDS_INPUT_DU1, | ||
27 | RCAR_LVDS_INPUT_DU2, | ||
28 | }; | ||
29 | |||
30 | #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) | ||
31 | int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); | ||
32 | int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, | ||
33 | struct drm_crtc *crtc, int mode); | ||
34 | #else | ||
35 | static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) | ||
36 | { | ||
37 | return 0; | ||
38 | } | ||
39 | static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, | ||
40 | struct drm_crtc *crtc, int mode) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | #endif | ||
45 | |||
46 | #endif /* __RCAR_DU_LVDSENC_H__ */ | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h new file mode 100644 index 000000000000..77cf9289ab65 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Electronics Corporation | ||
5 | * | ||
6 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 | ||
10 | * as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __RCAR_LVDS_REGS_H__ | ||
14 | #define __RCAR_LVDS_REGS_H__ | ||
15 | |||
16 | #define LVDCR0 0x0000 | ||
17 | #define LVDCR0_DUSEL (1 << 15) | ||
18 | #define LVDCR0_DMD (1 << 12) | ||
19 | #define LVDCR0_LVMD_MASK (0xf << 8) | ||
20 | #define LVDCR0_LVMD_SHIFT 8 | ||
21 | #define LVDCR0_PLLEN (1 << 4) | ||
22 | #define LVDCR0_BEN (1 << 2) | ||
23 | #define LVDCR0_LVEN (1 << 1) | ||
24 | #define LVDCR0_LVRES (1 << 0) | ||
25 | |||
26 | #define LVDCR1 0x0004 | ||
27 | #define LVDCR1_CKSEL (1 << 15) | ||
28 | #define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2)) | ||
29 | #define LVDCR1_CLKSTBY (3 << 0) | ||
30 | |||
31 | #define LVDPLLCR 0x0008 | ||
32 | #define LVDPLLCR_CEEN (1 << 14) | ||
33 | #define LVDPLLCR_FBEN (1 << 13) | ||
34 | #define LVDPLLCR_COSEL (1 << 12) | ||
35 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) | ||
36 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) | ||
37 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) | ||
38 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) | ||
39 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) | ||
40 | |||
41 | #define LVDCTRCR 0x000c | ||
42 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) | ||
43 | #define LVDCTRCR_CTR3SEL_ODD (1 << 12) | ||
44 | #define LVDCTRCR_CTR3SEL_CDE (2 << 12) | ||
45 | #define LVDCTRCR_CTR3SEL_MASK (7 << 12) | ||
46 | #define LVDCTRCR_CTR2SEL_DISP (0 << 8) | ||
47 | #define LVDCTRCR_CTR2SEL_ODD (1 << 8) | ||
48 | #define LVDCTRCR_CTR2SEL_CDE (2 << 8) | ||
49 | #define LVDCTRCR_CTR2SEL_HSYNC (3 << 8) | ||
50 | #define LVDCTRCR_CTR2SEL_VSYNC (4 << 8) | ||
51 | #define LVDCTRCR_CTR2SEL_MASK (7 << 8) | ||
52 | #define LVDCTRCR_CTR1SEL_VSYNC (0 << 4) | ||
53 | #define LVDCTRCR_CTR1SEL_DISP (1 << 4) | ||
54 | #define LVDCTRCR_CTR1SEL_ODD (2 << 4) | ||
55 | #define LVDCTRCR_CTR1SEL_CDE (3 << 4) | ||
56 | #define LVDCTRCR_CTR1SEL_HSYNC (4 << 4) | ||
57 | #define LVDCTRCR_CTR1SEL_MASK (7 << 4) | ||
58 | #define LVDCTRCR_CTR0SEL_HSYNC (0 << 0) | ||
59 | #define LVDCTRCR_CTR0SEL_VSYNC (1 << 0) | ||
60 | #define LVDCTRCR_CTR0SEL_DISP (2 << 0) | ||
61 | #define LVDCTRCR_CTR0SEL_ODD (3 << 0) | ||
62 | #define LVDCTRCR_CTR0SEL_CDE (4 << 0) | ||
63 | #define LVDCTRCR_CTR0SEL_MASK (7 << 0) | ||
64 | |||
65 | #define LVDCHCR 0x0010 | ||
66 | #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) | ||
67 | #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) | ||
68 | |||
69 | #endif /* __RCAR_LVDS_REGS_H__ */ | ||