diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/tegra/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dsi.c | 963 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dsi.h | 134 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/mipi-phy.c | 137 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/mipi-phy.h | 65 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/output.c | 5 |
10 files changed, 1320 insertions, 1 deletions
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig index 7a3e8ae90631..5acb2504efb1 100644 --- a/drivers/gpu/drm/tegra/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig | |||
@@ -6,6 +6,7 @@ config DRM_TEGRA | |||
6 | select TEGRA_HOST1X | 6 | select TEGRA_HOST1X |
7 | select DRM_KMS_HELPER | 7 | select DRM_KMS_HELPER |
8 | select DRM_KMS_FB_HELPER | 8 | select DRM_KMS_FB_HELPER |
9 | select DRM_MIPI_DSI | ||
9 | select DRM_PANEL | 10 | select DRM_PANEL |
10 | select FB_SYS_FILLRECT | 11 | select FB_SYS_FILLRECT |
11 | select FB_SYS_COPYAREA | 12 | select FB_SYS_COPYAREA |
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index edc76abd58bb..8d220afbd85f 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile | |||
@@ -9,6 +9,8 @@ tegra-drm-y := \ | |||
9 | output.o \ | 9 | output.o \ |
10 | rgb.o \ | 10 | rgb.o \ |
11 | hdmi.o \ | 11 | hdmi.o \ |
12 | mipi-phy.o \ | ||
13 | dsi.o \ | ||
12 | gr2d.o \ | 14 | gr2d.o \ |
13 | gr3d.o | 15 | gr3d.o |
14 | 16 | ||
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 91bbda291470..788627a060d7 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h | |||
@@ -28,6 +28,7 @@ | |||
28 | #define DISP_CTRL_MODE_STOP (0 << 5) | 28 | #define DISP_CTRL_MODE_STOP (0 << 5) |
29 | #define DISP_CTRL_MODE_C_DISPLAY (1 << 5) | 29 | #define DISP_CTRL_MODE_C_DISPLAY (1 << 5) |
30 | #define DISP_CTRL_MODE_NC_DISPLAY (2 << 5) | 30 | #define DISP_CTRL_MODE_NC_DISPLAY (2 << 5) |
31 | #define DISP_CTRL_MODE_MASK (3 << 5) | ||
31 | #define DC_CMD_SIGNAL_RAISE 0x033 | 32 | #define DC_CMD_SIGNAL_RAISE 0x033 |
32 | #define DC_CMD_DISPLAY_POWER_CONTROL 0x036 | 33 | #define DC_CMD_DISPLAY_POWER_CONTROL 0x036 |
33 | #define PW0_ENABLE (1 << 0) | 34 | #define PW0_ENABLE (1 << 0) |
@@ -116,6 +117,7 @@ | |||
116 | 117 | ||
117 | #define DC_DISP_DISP_WIN_OPTIONS 0x402 | 118 | #define DC_DISP_DISP_WIN_OPTIONS 0x402 |
118 | #define HDMI_ENABLE (1 << 30) | 119 | #define HDMI_ENABLE (1 << 30) |
120 | #define DSI_ENABLE (1 << 29) | ||
119 | 121 | ||
120 | #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 | 122 | #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 |
121 | #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) | 123 | #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) |
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 07eba596d458..08e9e3740c13 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -653,6 +653,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { | |||
653 | { .compatible = "nvidia,tegra30-hdmi", }, | 653 | { .compatible = "nvidia,tegra30-hdmi", }, |
654 | { .compatible = "nvidia,tegra30-gr2d", }, | 654 | { .compatible = "nvidia,tegra30-gr2d", }, |
655 | { .compatible = "nvidia,tegra30-gr3d", }, | 655 | { .compatible = "nvidia,tegra30-gr3d", }, |
656 | { .compatible = "nvidia,tegra114-dsi", }, | ||
656 | { .compatible = "nvidia,tegra114-hdmi", }, | 657 | { .compatible = "nvidia,tegra114-hdmi", }, |
657 | { .compatible = "nvidia,tegra114-gr3d", }, | 658 | { .compatible = "nvidia,tegra114-gr3d", }, |
658 | { /* sentinel */ } | 659 | { /* sentinel */ } |
@@ -677,10 +678,14 @@ static int __init host1x_drm_init(void) | |||
677 | if (err < 0) | 678 | if (err < 0) |
678 | goto unregister_host1x; | 679 | goto unregister_host1x; |
679 | 680 | ||
680 | err = platform_driver_register(&tegra_hdmi_driver); | 681 | err = platform_driver_register(&tegra_dsi_driver); |
681 | if (err < 0) | 682 | if (err < 0) |
682 | goto unregister_dc; | 683 | goto unregister_dc; |
683 | 684 | ||
685 | err = platform_driver_register(&tegra_hdmi_driver); | ||
686 | if (err < 0) | ||
687 | goto unregister_dsi; | ||
688 | |||
684 | err = platform_driver_register(&tegra_gr2d_driver); | 689 | err = platform_driver_register(&tegra_gr2d_driver); |
685 | if (err < 0) | 690 | if (err < 0) |
686 | goto unregister_hdmi; | 691 | goto unregister_hdmi; |
@@ -695,6 +700,8 @@ unregister_gr2d: | |||
695 | platform_driver_unregister(&tegra_gr2d_driver); | 700 | platform_driver_unregister(&tegra_gr2d_driver); |
696 | unregister_hdmi: | 701 | unregister_hdmi: |
697 | platform_driver_unregister(&tegra_hdmi_driver); | 702 | platform_driver_unregister(&tegra_hdmi_driver); |
703 | unregister_dsi: | ||
704 | platform_driver_unregister(&tegra_dsi_driver); | ||
698 | unregister_dc: | 705 | unregister_dc: |
699 | platform_driver_unregister(&tegra_dc_driver); | 706 | platform_driver_unregister(&tegra_dc_driver); |
700 | unregister_host1x: | 707 | unregister_host1x: |
@@ -708,6 +715,7 @@ static void __exit host1x_drm_exit(void) | |||
708 | platform_driver_unregister(&tegra_gr3d_driver); | 715 | platform_driver_unregister(&tegra_gr3d_driver); |
709 | platform_driver_unregister(&tegra_gr2d_driver); | 716 | platform_driver_unregister(&tegra_gr2d_driver); |
710 | platform_driver_unregister(&tegra_hdmi_driver); | 717 | platform_driver_unregister(&tegra_hdmi_driver); |
718 | platform_driver_unregister(&tegra_dsi_driver); | ||
711 | platform_driver_unregister(&tegra_dc_driver); | 719 | platform_driver_unregister(&tegra_dc_driver); |
712 | host1x_driver_unregister(&host1x_drm_driver); | 720 | host1x_driver_unregister(&host1x_drm_driver); |
713 | } | 721 | } |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index bb8b1305606e..ddaa937836de 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -177,6 +177,7 @@ struct tegra_output_ops { | |||
177 | enum tegra_output_type { | 177 | enum tegra_output_type { |
178 | TEGRA_OUTPUT_RGB, | 178 | TEGRA_OUTPUT_RGB, |
179 | TEGRA_OUTPUT_HDMI, | 179 | TEGRA_OUTPUT_HDMI, |
180 | TEGRA_OUTPUT_DSI, | ||
180 | }; | 181 | }; |
181 | 182 | ||
182 | struct tegra_output { | 183 | struct tegra_output { |
@@ -267,6 +268,7 @@ extern void tegra_drm_fb_exit(struct drm_device *drm); | |||
267 | extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); | 268 | extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); |
268 | 269 | ||
269 | extern struct platform_driver tegra_dc_driver; | 270 | extern struct platform_driver tegra_dc_driver; |
271 | extern struct platform_driver tegra_dsi_driver; | ||
270 | extern struct platform_driver tegra_hdmi_driver; | 272 | extern struct platform_driver tegra_hdmi_driver; |
271 | extern struct platform_driver tegra_gr2d_driver; | 273 | extern struct platform_driver tegra_gr2d_driver; |
272 | extern struct platform_driver tegra_gr3d_driver; | 274 | extern struct platform_driver tegra_gr3d_driver; |
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c new file mode 100644 index 000000000000..84a73e32214f --- /dev/null +++ b/drivers/gpu/drm/tegra/dsi.c | |||
@@ -0,0 +1,963 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 NVIDIA Corporation | ||
3 | * | ||
4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
5 | * documentation for any purpose is hereby granted without fee, provided that | ||
6 | * the above copyright notice appear in all copies and that both that copyright | ||
7 | * notice and this permission notice appear in supporting documentation, and | ||
8 | * that the name of the copyright holders not be used in advertising or | ||
9 | * publicity pertaining to distribution of the software without specific, | ||
10 | * written prior permission. The copyright holders make no representations | ||
11 | * about the suitability of this software for any purpose. It is provided "as | ||
12 | * is" without express or implied warranty. | ||
13 | * | ||
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
20 | * OF THIS SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include <linux/clk.h> | ||
24 | #include <linux/debugfs.h> | ||
25 | #include <linux/host1x.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/of.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/reset.h> | ||
30 | |||
31 | #include <drm/drm_mipi_dsi.h> | ||
32 | #include <drm/drm_panel.h> | ||
33 | |||
34 | #include <video/mipi_display.h> | ||
35 | |||
36 | #include "dc.h" | ||
37 | #include "drm.h" | ||
38 | #include "dsi.h" | ||
39 | #include "mipi-phy.h" | ||
40 | |||
41 | #define DSI_VIDEO_FIFO_DEPTH (1920 / 4) | ||
42 | #define DSI_HOST_FIFO_DEPTH 64 | ||
43 | |||
44 | struct tegra_dsi { | ||
45 | struct host1x_client client; | ||
46 | struct tegra_output output; | ||
47 | struct device *dev; | ||
48 | |||
49 | void __iomem *regs; | ||
50 | |||
51 | struct reset_control *rst; | ||
52 | struct clk *clk_parent; | ||
53 | struct clk *clk_lp; | ||
54 | struct clk *clk; | ||
55 | |||
56 | struct drm_info_list *debugfs_files; | ||
57 | struct drm_minor *minor; | ||
58 | struct dentry *debugfs; | ||
59 | |||
60 | enum mipi_dsi_pixel_format format; | ||
61 | unsigned int lanes; | ||
62 | |||
63 | struct tegra_mipi_device *mipi; | ||
64 | struct mipi_dsi_host host; | ||
65 | }; | ||
66 | |||
67 | static inline struct tegra_dsi * | ||
68 | host1x_client_to_dsi(struct host1x_client *client) | ||
69 | { | ||
70 | return container_of(client, struct tegra_dsi, client); | ||
71 | } | ||
72 | |||
73 | static inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host) | ||
74 | { | ||
75 | return container_of(host, struct tegra_dsi, host); | ||
76 | } | ||
77 | |||
78 | static inline struct tegra_dsi *to_dsi(struct tegra_output *output) | ||
79 | { | ||
80 | return container_of(output, struct tegra_dsi, output); | ||
81 | } | ||
82 | |||
83 | static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi, | ||
84 | unsigned long reg) | ||
85 | { | ||
86 | return readl(dsi->regs + (reg << 2)); | ||
87 | } | ||
88 | |||
89 | static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value, | ||
90 | unsigned long reg) | ||
91 | { | ||
92 | writel(value, dsi->regs + (reg << 2)); | ||
93 | } | ||
94 | |||
95 | static int tegra_dsi_show_regs(struct seq_file *s, void *data) | ||
96 | { | ||
97 | struct drm_info_node *node = s->private; | ||
98 | struct tegra_dsi *dsi = node->info_ent->data; | ||
99 | |||
100 | #define DUMP_REG(name) \ | ||
101 | seq_printf(s, "%-32s %#05x %08lx\n", #name, name, \ | ||
102 | tegra_dsi_readl(dsi, name)) | ||
103 | |||
104 | DUMP_REG(DSI_INCR_SYNCPT); | ||
105 | DUMP_REG(DSI_INCR_SYNCPT_CONTROL); | ||
106 | DUMP_REG(DSI_INCR_SYNCPT_ERROR); | ||
107 | DUMP_REG(DSI_CTXSW); | ||
108 | DUMP_REG(DSI_RD_DATA); | ||
109 | DUMP_REG(DSI_WR_DATA); | ||
110 | DUMP_REG(DSI_POWER_CONTROL); | ||
111 | DUMP_REG(DSI_INT_ENABLE); | ||
112 | DUMP_REG(DSI_INT_STATUS); | ||
113 | DUMP_REG(DSI_INT_MASK); | ||
114 | DUMP_REG(DSI_HOST_CONTROL); | ||
115 | DUMP_REG(DSI_CONTROL); | ||
116 | DUMP_REG(DSI_SOL_DELAY); | ||
117 | DUMP_REG(DSI_MAX_THRESHOLD); | ||
118 | DUMP_REG(DSI_TRIGGER); | ||
119 | DUMP_REG(DSI_TX_CRC); | ||
120 | DUMP_REG(DSI_STATUS); | ||
121 | |||
122 | DUMP_REG(DSI_INIT_SEQ_CONTROL); | ||
123 | DUMP_REG(DSI_INIT_SEQ_DATA_0); | ||
124 | DUMP_REG(DSI_INIT_SEQ_DATA_1); | ||
125 | DUMP_REG(DSI_INIT_SEQ_DATA_2); | ||
126 | DUMP_REG(DSI_INIT_SEQ_DATA_3); | ||
127 | DUMP_REG(DSI_INIT_SEQ_DATA_4); | ||
128 | DUMP_REG(DSI_INIT_SEQ_DATA_5); | ||
129 | DUMP_REG(DSI_INIT_SEQ_DATA_6); | ||
130 | DUMP_REG(DSI_INIT_SEQ_DATA_7); | ||
131 | |||
132 | DUMP_REG(DSI_PKT_SEQ_0_LO); | ||
133 | DUMP_REG(DSI_PKT_SEQ_0_HI); | ||
134 | DUMP_REG(DSI_PKT_SEQ_1_LO); | ||
135 | DUMP_REG(DSI_PKT_SEQ_1_HI); | ||
136 | DUMP_REG(DSI_PKT_SEQ_2_LO); | ||
137 | DUMP_REG(DSI_PKT_SEQ_2_HI); | ||
138 | DUMP_REG(DSI_PKT_SEQ_3_LO); | ||
139 | DUMP_REG(DSI_PKT_SEQ_3_HI); | ||
140 | DUMP_REG(DSI_PKT_SEQ_4_LO); | ||
141 | DUMP_REG(DSI_PKT_SEQ_4_HI); | ||
142 | DUMP_REG(DSI_PKT_SEQ_5_LO); | ||
143 | DUMP_REG(DSI_PKT_SEQ_5_HI); | ||
144 | |||
145 | DUMP_REG(DSI_DCS_CMDS); | ||
146 | |||
147 | DUMP_REG(DSI_PKT_LEN_0_1); | ||
148 | DUMP_REG(DSI_PKT_LEN_2_3); | ||
149 | DUMP_REG(DSI_PKT_LEN_4_5); | ||
150 | DUMP_REG(DSI_PKT_LEN_6_7); | ||
151 | |||
152 | DUMP_REG(DSI_PHY_TIMING_0); | ||
153 | DUMP_REG(DSI_PHY_TIMING_1); | ||
154 | DUMP_REG(DSI_PHY_TIMING_2); | ||
155 | DUMP_REG(DSI_BTA_TIMING); | ||
156 | |||
157 | DUMP_REG(DSI_TIMEOUT_0); | ||
158 | DUMP_REG(DSI_TIMEOUT_1); | ||
159 | DUMP_REG(DSI_TO_TALLY); | ||
160 | |||
161 | DUMP_REG(DSI_PAD_CONTROL_0); | ||
162 | DUMP_REG(DSI_PAD_CONTROL_CD); | ||
163 | DUMP_REG(DSI_PAD_CD_STATUS); | ||
164 | DUMP_REG(DSI_VIDEO_MODE_CONTROL); | ||
165 | DUMP_REG(DSI_PAD_CONTROL_1); | ||
166 | DUMP_REG(DSI_PAD_CONTROL_2); | ||
167 | DUMP_REG(DSI_PAD_CONTROL_3); | ||
168 | DUMP_REG(DSI_PAD_CONTROL_4); | ||
169 | |||
170 | DUMP_REG(DSI_GANGED_MODE_CONTROL); | ||
171 | DUMP_REG(DSI_GANGED_MODE_START); | ||
172 | DUMP_REG(DSI_GANGED_MODE_SIZE); | ||
173 | |||
174 | DUMP_REG(DSI_RAW_DATA_BYTE_COUNT); | ||
175 | DUMP_REG(DSI_ULTRA_LOW_POWER_CONTROL); | ||
176 | |||
177 | DUMP_REG(DSI_INIT_SEQ_DATA_8); | ||
178 | DUMP_REG(DSI_INIT_SEQ_DATA_9); | ||
179 | DUMP_REG(DSI_INIT_SEQ_DATA_10); | ||
180 | DUMP_REG(DSI_INIT_SEQ_DATA_11); | ||
181 | DUMP_REG(DSI_INIT_SEQ_DATA_12); | ||
182 | DUMP_REG(DSI_INIT_SEQ_DATA_13); | ||
183 | DUMP_REG(DSI_INIT_SEQ_DATA_14); | ||
184 | DUMP_REG(DSI_INIT_SEQ_DATA_15); | ||
185 | |||
186 | #undef DUMP_REG | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static struct drm_info_list debugfs_files[] = { | ||
192 | { "regs", tegra_dsi_show_regs, 0, NULL }, | ||
193 | }; | ||
194 | |||
195 | static int tegra_dsi_debugfs_init(struct tegra_dsi *dsi, | ||
196 | struct drm_minor *minor) | ||
197 | { | ||
198 | const char *name = dev_name(dsi->dev); | ||
199 | unsigned int i; | ||
200 | int err; | ||
201 | |||
202 | dsi->debugfs = debugfs_create_dir(name, minor->debugfs_root); | ||
203 | if (!dsi->debugfs) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | dsi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), | ||
207 | GFP_KERNEL); | ||
208 | if (!dsi->debugfs_files) { | ||
209 | err = -ENOMEM; | ||
210 | goto remove; | ||
211 | } | ||
212 | |||
213 | for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) | ||
214 | dsi->debugfs_files[i].data = dsi; | ||
215 | |||
216 | err = drm_debugfs_create_files(dsi->debugfs_files, | ||
217 | ARRAY_SIZE(debugfs_files), | ||
218 | dsi->debugfs, minor); | ||
219 | if (err < 0) | ||
220 | goto free; | ||
221 | |||
222 | dsi->minor = minor; | ||
223 | |||
224 | return 0; | ||
225 | |||
226 | free: | ||
227 | kfree(dsi->debugfs_files); | ||
228 | dsi->debugfs_files = NULL; | ||
229 | remove: | ||
230 | debugfs_remove(dsi->debugfs); | ||
231 | dsi->debugfs = NULL; | ||
232 | |||
233 | return err; | ||
234 | } | ||
235 | |||
236 | static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) | ||
237 | { | ||
238 | drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files), | ||
239 | dsi->minor); | ||
240 | dsi->minor = NULL; | ||
241 | |||
242 | kfree(dsi->debugfs_files); | ||
243 | dsi->debugfs_files = NULL; | ||
244 | |||
245 | debugfs_remove(dsi->debugfs); | ||
246 | dsi->debugfs = NULL; | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | #define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) | ||
252 | #define PKT_LEN0(len) (((len) & 0x07) << 0) | ||
253 | #define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19)) | ||
254 | #define PKT_LEN1(len) (((len) & 0x07) << 10) | ||
255 | #define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29)) | ||
256 | #define PKT_LEN2(len) (((len) & 0x07) << 20) | ||
257 | |||
258 | #define PKT_LP (1 << 30) | ||
259 | #define NUM_PKT_SEQ 12 | ||
260 | |||
261 | /* non-burst mode with sync-end */ | ||
262 | static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = { | ||
263 | [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | | ||
264 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
265 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | | ||
266 | PKT_LP, | ||
267 | [ 1] = 0, | ||
268 | [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) | | ||
269 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
270 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | | ||
271 | PKT_LP, | ||
272 | [ 3] = 0, | ||
273 | [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | | ||
274 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
275 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | | ||
276 | PKT_LP, | ||
277 | [ 5] = 0, | ||
278 | [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | | ||
279 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
280 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), | ||
281 | [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | | ||
282 | PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | | ||
283 | PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), | ||
284 | [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | | ||
285 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
286 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | | ||
287 | PKT_LP, | ||
288 | [ 9] = 0, | ||
289 | [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | | ||
290 | PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | | ||
291 | PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), | ||
292 | [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | | ||
293 | PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | | ||
294 | PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), | ||
295 | }; | ||
296 | |||
297 | static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) | ||
298 | { | ||
299 | struct mipi_dphy_timing timing; | ||
300 | unsigned long value, period; | ||
301 | long rate; | ||
302 | int err; | ||
303 | |||
304 | rate = clk_get_rate(dsi->clk); | ||
305 | if (rate < 0) | ||
306 | return rate; | ||
307 | |||
308 | period = DIV_ROUND_CLOSEST(1000000000UL, rate * 2); | ||
309 | |||
310 | err = mipi_dphy_timing_get_default(&timing, period); | ||
311 | if (err < 0) | ||
312 | return err; | ||
313 | |||
314 | err = mipi_dphy_timing_validate(&timing, period); | ||
315 | if (err < 0) { | ||
316 | dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err); | ||
317 | return err; | ||
318 | } | ||
319 | |||
320 | /* | ||
321 | * The D-PHY timing fields below are expressed in byte-clock cycles, | ||
322 | * so multiply the period by 8. | ||
323 | */ | ||
324 | period *= 8; | ||
325 | |||
326 | value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 | | ||
327 | DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 | | ||
328 | DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 | | ||
329 | DSI_TIMING_FIELD(timing.hsprepare, period, 1); | ||
330 | tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0); | ||
331 | |||
332 | value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 | | ||
333 | DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 | | ||
334 | DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 | | ||
335 | DSI_TIMING_FIELD(timing.lpx, period, 1); | ||
336 | tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1); | ||
337 | |||
338 | value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 | | ||
339 | DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 | | ||
340 | DSI_TIMING_FIELD(0xff * period, period, 0) << 0; | ||
341 | tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2); | ||
342 | |||
343 | value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 | | ||
344 | DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 | | ||
345 | DSI_TIMING_FIELD(timing.tago, period, 1); | ||
346 | tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, | ||
352 | unsigned int *mulp, unsigned int *divp) | ||
353 | { | ||
354 | switch (format) { | ||
355 | case MIPI_DSI_FMT_RGB666_PACKED: | ||
356 | case MIPI_DSI_FMT_RGB888: | ||
357 | *mulp = 3; | ||
358 | *divp = 1; | ||
359 | break; | ||
360 | |||
361 | case MIPI_DSI_FMT_RGB565: | ||
362 | *mulp = 2; | ||
363 | *divp = 1; | ||
364 | break; | ||
365 | |||
366 | case MIPI_DSI_FMT_RGB666: | ||
367 | *mulp = 9; | ||
368 | *divp = 4; | ||
369 | break; | ||
370 | |||
371 | default: | ||
372 | return -EINVAL; | ||
373 | } | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static int tegra_output_dsi_enable(struct tegra_output *output) | ||
379 | { | ||
380 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | ||
381 | struct drm_display_mode *mode = &dc->base.mode; | ||
382 | unsigned int hact, hsw, hbp, hfp, i, mul, div; | ||
383 | struct tegra_dsi *dsi = to_dsi(output); | ||
384 | /* FIXME: don't hardcode this */ | ||
385 | const u32 *pkt_seq = pkt_seq_vnb_syne; | ||
386 | unsigned long value; | ||
387 | int err; | ||
388 | |||
389 | err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); | ||
390 | if (err < 0) | ||
391 | return err; | ||
392 | |||
393 | err = clk_enable(dsi->clk); | ||
394 | if (err < 0) | ||
395 | return err; | ||
396 | |||
397 | reset_control_deassert(dsi->rst); | ||
398 | |||
399 | value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) | | ||
400 | DSI_CONTROL_LANES(dsi->lanes - 1) | | ||
401 | DSI_CONTROL_SOURCE(dc->pipe); | ||
402 | tegra_dsi_writel(dsi, value, DSI_CONTROL); | ||
403 | |||
404 | tegra_dsi_writel(dsi, DSI_VIDEO_FIFO_DEPTH, DSI_MAX_THRESHOLD); | ||
405 | |||
406 | value = DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | | ||
407 | DSI_HOST_CONTROL_ECC; | ||
408 | tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); | ||
409 | |||
410 | value = tegra_dsi_readl(dsi, DSI_CONTROL); | ||
411 | value |= DSI_CONTROL_HS_CLK_CTRL; | ||
412 | value &= ~DSI_CONTROL_TX_TRIG(3); | ||
413 | value &= ~DSI_CONTROL_DCS_ENABLE; | ||
414 | value |= DSI_CONTROL_VIDEO_ENABLE; | ||
415 | value &= ~DSI_CONTROL_HOST_ENABLE; | ||
416 | tegra_dsi_writel(dsi, value, DSI_CONTROL); | ||
417 | |||
418 | err = tegra_dsi_set_phy_timing(dsi); | ||
419 | if (err < 0) | ||
420 | return err; | ||
421 | |||
422 | for (i = 0; i < NUM_PKT_SEQ; i++) | ||
423 | tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); | ||
424 | |||
425 | /* horizontal active pixels */ | ||
426 | hact = mode->hdisplay * mul / div; | ||
427 | |||
428 | /* horizontal sync width */ | ||
429 | hsw = (mode->hsync_end - mode->hsync_start) * mul / div; | ||
430 | hsw -= 10; | ||
431 | |||
432 | /* horizontal back porch */ | ||
433 | hbp = (mode->htotal - mode->hsync_end) * mul / div; | ||
434 | hbp -= 14; | ||
435 | |||
436 | /* horizontal front porch */ | ||
437 | hfp = (mode->hsync_start - mode->hdisplay) * mul / div; | ||
438 | hfp -= 8; | ||
439 | |||
440 | tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); | ||
441 | tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); | ||
442 | tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); | ||
443 | tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7); | ||
444 | |||
445 | /* set SOL delay */ | ||
446 | tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); | ||
447 | |||
448 | /* enable display controller */ | ||
449 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
450 | value |= DSI_ENABLE; | ||
451 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
452 | |||
453 | value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
454 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; | ||
455 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); | ||
456 | |||
457 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); | ||
458 | value &= ~DISP_CTRL_MODE_MASK; | ||
459 | value |= DISP_CTRL_MODE_C_DISPLAY; | ||
460 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); | ||
461 | |||
462 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
463 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
464 | |||
465 | /* enable DSI controller */ | ||
466 | value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); | ||
467 | value |= DSI_POWER_CONTROL_ENABLE; | ||
468 | tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int tegra_output_dsi_disable(struct tegra_output *output) | ||
474 | { | ||
475 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | ||
476 | struct tegra_dsi *dsi = to_dsi(output); | ||
477 | unsigned long value; | ||
478 | |||
479 | /* disable DSI controller */ | ||
480 | value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); | ||
481 | value &= DSI_POWER_CONTROL_ENABLE; | ||
482 | tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); | ||
483 | |||
484 | /* | ||
485 | * FIXME: The output isn't attached to any CRTC when it's being | ||
486 | * disabled, so the following will never be executed. | ||
487 | */ | ||
488 | if (dc) { | ||
489 | /* disable display controller */ | ||
490 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); | ||
491 | value &= ~DISP_CTRL_MODE_MASK; | ||
492 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); | ||
493 | |||
494 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
495 | value &= ~DSI_ENABLE; | ||
496 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
497 | } | ||
498 | |||
499 | clk_disable(dsi->clk); | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static int tegra_output_dsi_setup_clock(struct tegra_output *output, | ||
505 | struct clk *clk, unsigned long pclk) | ||
506 | { | ||
507 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | ||
508 | struct drm_display_mode *mode = &dc->base.mode; | ||
509 | unsigned int timeout, mul, div, vrefresh; | ||
510 | struct tegra_dsi *dsi = to_dsi(output); | ||
511 | unsigned long bclk, plld, value; | ||
512 | struct clk *base; | ||
513 | int err; | ||
514 | |||
515 | err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); | ||
516 | if (err < 0) | ||
517 | return err; | ||
518 | |||
519 | vrefresh = drm_mode_vrefresh(mode); | ||
520 | |||
521 | pclk = mode->htotal * mode->vtotal * vrefresh; | ||
522 | bclk = (pclk * mul) / (div * dsi->lanes); | ||
523 | plld = DIV_ROUND_UP(bclk * 8, 1000000); | ||
524 | pclk = (plld * 1000000) / 2; | ||
525 | |||
526 | err = clk_set_parent(clk, dsi->clk_parent); | ||
527 | if (err < 0) { | ||
528 | dev_err(dsi->dev, "failed to set parent clock: %d\n", err); | ||
529 | return err; | ||
530 | } | ||
531 | |||
532 | base = clk_get_parent(dsi->clk_parent); | ||
533 | |||
534 | /* | ||
535 | * This assumes that the parent clock is pll_d_out0 or pll_d2_out | ||
536 | * respectively, each of which divides the base pll_d by 2. | ||
537 | */ | ||
538 | err = clk_set_rate(base, pclk * 2); | ||
539 | if (err < 0) { | ||
540 | dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n", | ||
541 | pclk * 2); | ||
542 | return err; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * XXX: Move the below somewhere else so that we don't need to have | ||
547 | * access to the vrefresh in this function? | ||
548 | */ | ||
549 | |||
550 | /* one frame high-speed transmission timeout */ | ||
551 | timeout = (bclk / vrefresh) / 512; | ||
552 | value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); | ||
553 | tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); | ||
554 | |||
555 | /* 2 ms peripheral timeout for panel */ | ||
556 | timeout = 2 * bclk / 512 * 1000; | ||
557 | value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); | ||
558 | tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); | ||
559 | |||
560 | value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); | ||
561 | tegra_dsi_writel(dsi, value, DSI_TO_TALLY); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static int tegra_output_dsi_check_mode(struct tegra_output *output, | ||
567 | struct drm_display_mode *mode, | ||
568 | enum drm_mode_status *status) | ||
569 | { | ||
570 | /* | ||
571 | * FIXME: For now, always assume that the mode is okay. | ||
572 | */ | ||
573 | |||
574 | *status = MODE_OK; | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static const struct tegra_output_ops dsi_ops = { | ||
580 | .enable = tegra_output_dsi_enable, | ||
581 | .disable = tegra_output_dsi_disable, | ||
582 | .setup_clock = tegra_output_dsi_setup_clock, | ||
583 | .check_mode = tegra_output_dsi_check_mode, | ||
584 | }; | ||
585 | |||
586 | static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) | ||
587 | { | ||
588 | unsigned long value; | ||
589 | |||
590 | value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); | ||
591 | tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) | ||
597 | { | ||
598 | unsigned long value; | ||
599 | |||
600 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); | ||
601 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); | ||
602 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); | ||
603 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); | ||
604 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); | ||
605 | |||
606 | /* start calibration */ | ||
607 | tegra_dsi_pad_enable(dsi); | ||
608 | |||
609 | value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | | ||
610 | DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | | ||
611 | DSI_PAD_OUT_CLK(0x0); | ||
612 | tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); | ||
613 | |||
614 | return tegra_mipi_calibrate(dsi->mipi); | ||
615 | } | ||
616 | |||
617 | static int tegra_dsi_init(struct host1x_client *client) | ||
618 | { | ||
619 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); | ||
620 | struct tegra_dsi *dsi = host1x_client_to_dsi(client); | ||
621 | unsigned long value, i; | ||
622 | int err; | ||
623 | |||
624 | dsi->output.type = TEGRA_OUTPUT_DSI; | ||
625 | dsi->output.dev = client->dev; | ||
626 | dsi->output.ops = &dsi_ops; | ||
627 | |||
628 | err = tegra_output_init(tegra->drm, &dsi->output); | ||
629 | if (err < 0) { | ||
630 | dev_err(client->dev, "output setup failed: %d\n", err); | ||
631 | return err; | ||
632 | } | ||
633 | |||
634 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | ||
635 | err = tegra_dsi_debugfs_init(dsi, tegra->drm->primary); | ||
636 | if (err < 0) | ||
637 | dev_err(dsi->dev, "debugfs setup failed: %d\n", err); | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * enable high-speed mode, checksum generation, ECC generation and | ||
642 | * disable raw mode | ||
643 | */ | ||
644 | value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL); | ||
645 | value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS | | ||
646 | DSI_HOST_CONTROL_HS; | ||
647 | value &= ~DSI_HOST_CONTROL_RAW; | ||
648 | tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); | ||
649 | |||
650 | tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY); | ||
651 | tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD); | ||
652 | |||
653 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); | ||
654 | |||
655 | for (i = 0; i < 8; i++) { | ||
656 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i); | ||
657 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i); | ||
658 | } | ||
659 | |||
660 | for (i = 0; i < 12; i++) | ||
661 | tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i); | ||
662 | |||
663 | tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS); | ||
664 | |||
665 | err = tegra_dsi_pad_calibrate(dsi); | ||
666 | if (err < 0) { | ||
667 | dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); | ||
668 | return err; | ||
669 | } | ||
670 | |||
671 | tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL); | ||
672 | usleep_range(300, 1000); | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static int tegra_dsi_exit(struct host1x_client *client) | ||
678 | { | ||
679 | struct tegra_dsi *dsi = host1x_client_to_dsi(client); | ||
680 | int err; | ||
681 | |||
682 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { | ||
683 | err = tegra_dsi_debugfs_exit(dsi); | ||
684 | if (err < 0) | ||
685 | dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err); | ||
686 | } | ||
687 | |||
688 | err = tegra_output_disable(&dsi->output); | ||
689 | if (err < 0) { | ||
690 | dev_err(client->dev, "output failed to disable: %d\n", err); | ||
691 | return err; | ||
692 | } | ||
693 | |||
694 | err = tegra_output_exit(&dsi->output); | ||
695 | if (err < 0) { | ||
696 | dev_err(client->dev, "output cleanup failed: %d\n", err); | ||
697 | return err; | ||
698 | } | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static const struct host1x_client_ops dsi_client_ops = { | ||
704 | .init = tegra_dsi_init, | ||
705 | .exit = tegra_dsi_exit, | ||
706 | }; | ||
707 | |||
708 | static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi) | ||
709 | { | ||
710 | struct clk *parent; | ||
711 | int err; | ||
712 | |||
713 | parent = clk_get_parent(dsi->clk); | ||
714 | if (!parent) | ||
715 | return -EINVAL; | ||
716 | |||
717 | err = clk_set_parent(parent, dsi->clk_parent); | ||
718 | if (err < 0) | ||
719 | return err; | ||
720 | |||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | static void tegra_dsi_initialize(struct tegra_dsi *dsi) | ||
725 | { | ||
726 | unsigned int i; | ||
727 | |||
728 | tegra_dsi_writel(dsi, 0, DSI_POWER_CONTROL); | ||
729 | |||
730 | tegra_dsi_writel(dsi, 0, DSI_INT_ENABLE); | ||
731 | tegra_dsi_writel(dsi, 0, DSI_INT_STATUS); | ||
732 | tegra_dsi_writel(dsi, 0, DSI_INT_MASK); | ||
733 | |||
734 | tegra_dsi_writel(dsi, 0, DSI_HOST_CONTROL); | ||
735 | tegra_dsi_writel(dsi, 0, DSI_CONTROL); | ||
736 | |||
737 | tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY); | ||
738 | tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD); | ||
739 | |||
740 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); | ||
741 | |||
742 | for (i = 0; i < 8; i++) { | ||
743 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i); | ||
744 | tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i); | ||
745 | } | ||
746 | |||
747 | for (i = 0; i < 12; i++) | ||
748 | tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i); | ||
749 | |||
750 | tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS); | ||
751 | |||
752 | for (i = 0; i < 4; i++) | ||
753 | tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1 + i); | ||
754 | |||
755 | tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_0); | ||
756 | tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_1); | ||
757 | tegra_dsi_writel(dsi, 0x000000ff, DSI_PHY_TIMING_2); | ||
758 | tegra_dsi_writel(dsi, 0x00000000, DSI_BTA_TIMING); | ||
759 | |||
760 | tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_0); | ||
761 | tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_1); | ||
762 | tegra_dsi_writel(dsi, 0, DSI_TO_TALLY); | ||
763 | |||
764 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); | ||
765 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_CD); | ||
766 | tegra_dsi_writel(dsi, 0, DSI_PAD_CD_STATUS); | ||
767 | tegra_dsi_writel(dsi, 0, DSI_VIDEO_MODE_CONTROL); | ||
768 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); | ||
769 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); | ||
770 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); | ||
771 | tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); | ||
772 | |||
773 | tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); | ||
774 | tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START); | ||
775 | tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE); | ||
776 | } | ||
777 | |||
778 | static int tegra_dsi_host_attach(struct mipi_dsi_host *host, | ||
779 | struct mipi_dsi_device *device) | ||
780 | { | ||
781 | struct tegra_dsi *dsi = host_to_tegra(host); | ||
782 | struct tegra_output *output = &dsi->output; | ||
783 | |||
784 | dsi->format = device->format; | ||
785 | dsi->lanes = device->lanes; | ||
786 | |||
787 | output->panel = of_drm_find_panel(device->dev.of_node); | ||
788 | if (output->panel) { | ||
789 | if (output->connector.dev) | ||
790 | drm_helper_hpd_irq_event(output->connector.dev); | ||
791 | } | ||
792 | |||
793 | return 0; | ||
794 | } | ||
795 | |||
796 | static int tegra_dsi_host_detach(struct mipi_dsi_host *host, | ||
797 | struct mipi_dsi_device *device) | ||
798 | { | ||
799 | struct tegra_dsi *dsi = host_to_tegra(host); | ||
800 | struct tegra_output *output = &dsi->output; | ||
801 | |||
802 | if (output->panel && &device->dev == output->panel->dev) { | ||
803 | if (output->connector.dev) | ||
804 | drm_helper_hpd_irq_event(output->connector.dev); | ||
805 | |||
806 | output->panel = NULL; | ||
807 | } | ||
808 | |||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | static const struct mipi_dsi_host_ops tegra_dsi_host_ops = { | ||
813 | .attach = tegra_dsi_host_attach, | ||
814 | .detach = tegra_dsi_host_detach, | ||
815 | }; | ||
816 | |||
817 | static int tegra_dsi_probe(struct platform_device *pdev) | ||
818 | { | ||
819 | struct tegra_dsi *dsi; | ||
820 | struct resource *regs; | ||
821 | int err; | ||
822 | |||
823 | dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); | ||
824 | if (!dsi) | ||
825 | return -ENOMEM; | ||
826 | |||
827 | dsi->output.dev = dsi->dev = &pdev->dev; | ||
828 | |||
829 | err = tegra_output_probe(&dsi->output); | ||
830 | if (err < 0) | ||
831 | return err; | ||
832 | |||
833 | /* | ||
834 | * Assume these values by default. When a DSI peripheral driver | ||
835 | * attaches to the DSI host, the parameters will be taken from | ||
836 | * the attached device. | ||
837 | */ | ||
838 | dsi->format = MIPI_DSI_FMT_RGB888; | ||
839 | dsi->lanes = 4; | ||
840 | |||
841 | dsi->rst = devm_reset_control_get(&pdev->dev, "dsi"); | ||
842 | if (IS_ERR(dsi->rst)) | ||
843 | return PTR_ERR(dsi->rst); | ||
844 | |||
845 | dsi->clk = devm_clk_get(&pdev->dev, NULL); | ||
846 | if (IS_ERR(dsi->clk)) { | ||
847 | dev_err(&pdev->dev, "cannot get DSI clock\n"); | ||
848 | return PTR_ERR(dsi->clk); | ||
849 | } | ||
850 | |||
851 | err = clk_prepare_enable(dsi->clk); | ||
852 | if (err < 0) { | ||
853 | dev_err(&pdev->dev, "cannot enable DSI clock\n"); | ||
854 | return err; | ||
855 | } | ||
856 | |||
857 | dsi->clk_lp = devm_clk_get(&pdev->dev, "lp"); | ||
858 | if (IS_ERR(dsi->clk_lp)) { | ||
859 | dev_err(&pdev->dev, "cannot get low-power clock\n"); | ||
860 | return PTR_ERR(dsi->clk_lp); | ||
861 | } | ||
862 | |||
863 | err = clk_prepare_enable(dsi->clk_lp); | ||
864 | if (err < 0) { | ||
865 | dev_err(&pdev->dev, "cannot enable low-power clock\n"); | ||
866 | return err; | ||
867 | } | ||
868 | |||
869 | dsi->clk_parent = devm_clk_get(&pdev->dev, "parent"); | ||
870 | if (IS_ERR(dsi->clk_parent)) { | ||
871 | dev_err(&pdev->dev, "cannot get parent clock\n"); | ||
872 | return PTR_ERR(dsi->clk_parent); | ||
873 | } | ||
874 | |||
875 | err = clk_prepare_enable(dsi->clk_parent); | ||
876 | if (err < 0) { | ||
877 | dev_err(&pdev->dev, "cannot enable parent clock\n"); | ||
878 | return err; | ||
879 | } | ||
880 | |||
881 | err = tegra_dsi_setup_clocks(dsi); | ||
882 | if (err < 0) { | ||
883 | dev_err(&pdev->dev, "cannot setup clocks\n"); | ||
884 | return err; | ||
885 | } | ||
886 | |||
887 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
888 | dsi->regs = devm_ioremap_resource(&pdev->dev, regs); | ||
889 | if (!dsi->regs) | ||
890 | return -EADDRNOTAVAIL; | ||
891 | |||
892 | tegra_dsi_initialize(dsi); | ||
893 | |||
894 | dsi->mipi = tegra_mipi_request(&pdev->dev); | ||
895 | if (IS_ERR(dsi->mipi)) | ||
896 | return PTR_ERR(dsi->mipi); | ||
897 | |||
898 | dsi->host.ops = &tegra_dsi_host_ops; | ||
899 | dsi->host.dev = &pdev->dev; | ||
900 | |||
901 | err = mipi_dsi_host_register(&dsi->host); | ||
902 | if (err < 0) { | ||
903 | dev_err(&pdev->dev, "failed to register DSI host: %d\n", err); | ||
904 | return err; | ||
905 | } | ||
906 | |||
907 | INIT_LIST_HEAD(&dsi->client.list); | ||
908 | dsi->client.ops = &dsi_client_ops; | ||
909 | dsi->client.dev = &pdev->dev; | ||
910 | |||
911 | err = host1x_client_register(&dsi->client); | ||
912 | if (err < 0) { | ||
913 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | ||
914 | err); | ||
915 | return err; | ||
916 | } | ||
917 | |||
918 | platform_set_drvdata(pdev, dsi); | ||
919 | |||
920 | return 0; | ||
921 | } | ||
922 | |||
923 | static int tegra_dsi_remove(struct platform_device *pdev) | ||
924 | { | ||
925 | struct tegra_dsi *dsi = platform_get_drvdata(pdev); | ||
926 | int err; | ||
927 | |||
928 | err = host1x_client_unregister(&dsi->client); | ||
929 | if (err < 0) { | ||
930 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | ||
931 | err); | ||
932 | return err; | ||
933 | } | ||
934 | |||
935 | mipi_dsi_host_unregister(&dsi->host); | ||
936 | tegra_mipi_free(dsi->mipi); | ||
937 | |||
938 | clk_disable_unprepare(dsi->clk_parent); | ||
939 | clk_disable_unprepare(dsi->clk_lp); | ||
940 | clk_disable_unprepare(dsi->clk); | ||
941 | |||
942 | err = tegra_output_remove(&dsi->output); | ||
943 | if (err < 0) { | ||
944 | dev_err(&pdev->dev, "failed to remove output: %d\n", err); | ||
945 | return err; | ||
946 | } | ||
947 | |||
948 | return 0; | ||
949 | } | ||
950 | |||
951 | static const struct of_device_id tegra_dsi_of_match[] = { | ||
952 | { .compatible = "nvidia,tegra114-dsi", }, | ||
953 | { }, | ||
954 | }; | ||
955 | |||
956 | struct platform_driver tegra_dsi_driver = { | ||
957 | .driver = { | ||
958 | .name = "tegra-dsi", | ||
959 | .of_match_table = tegra_dsi_of_match, | ||
960 | }, | ||
961 | .probe = tegra_dsi_probe, | ||
962 | .remove = tegra_dsi_remove, | ||
963 | }; | ||
diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h new file mode 100644 index 000000000000..00e79c1f448c --- /dev/null +++ b/drivers/gpu/drm/tegra/dsi.h | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 NVIDIA Corporation | ||
3 | * | ||
4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
5 | * documentation for any purpose is hereby granted without fee, provided that | ||
6 | * the above copyright notice appear in all copies and that both that copyright | ||
7 | * notice and this permission notice appear in supporting documentation, and | ||
8 | * that the name of the copyright holders not be used in advertising or | ||
9 | * publicity pertaining to distribution of the software without specific, | ||
10 | * written prior permission. The copyright holders make no representations | ||
11 | * about the suitability of this software for any purpose. It is provided "as | ||
12 | * is" without express or implied warranty. | ||
13 | * | ||
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
20 | * OF THIS SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #ifndef DRM_TEGRA_DSI_H | ||
24 | #define DRM_TEGRA_DSI_H | ||
25 | |||
26 | #define DSI_INCR_SYNCPT 0x00 | ||
27 | #define DSI_INCR_SYNCPT_CONTROL 0x01 | ||
28 | #define DSI_INCR_SYNCPT_ERROR 0x02 | ||
29 | #define DSI_CTXSW 0x08 | ||
30 | #define DSI_RD_DATA 0x09 | ||
31 | #define DSI_WR_DATA 0x0a | ||
32 | #define DSI_POWER_CONTROL 0x0b | ||
33 | #define DSI_POWER_CONTROL_ENABLE (1 << 0) | ||
34 | #define DSI_INT_ENABLE 0x0c | ||
35 | #define DSI_INT_STATUS 0x0d | ||
36 | #define DSI_INT_MASK 0x0e | ||
37 | #define DSI_HOST_CONTROL 0x0f | ||
38 | #define DSI_HOST_CONTROL_RAW (1 << 6) | ||
39 | #define DSI_HOST_CONTROL_HS (1 << 5) | ||
40 | #define DSI_HOST_CONTROL_BTA (1 << 2) | ||
41 | #define DSI_HOST_CONTROL_CS (1 << 1) | ||
42 | #define DSI_HOST_CONTROL_ECC (1 << 0) | ||
43 | #define DSI_CONTROL 0x10 | ||
44 | #define DSI_CONTROL_HS_CLK_CTRL (1 << 20) | ||
45 | #define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16) | ||
46 | #define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12) | ||
47 | #define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8) | ||
48 | #define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4) | ||
49 | #define DSI_CONTROL_DCS_ENABLE (1 << 3) | ||
50 | #define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2) | ||
51 | #define DSI_CONTROL_VIDEO_ENABLE (1 << 1) | ||
52 | #define DSI_CONTROL_HOST_ENABLE (1 << 0) | ||
53 | #define DSI_SOL_DELAY 0x11 | ||
54 | #define DSI_MAX_THRESHOLD 0x12 | ||
55 | #define DSI_TRIGGER 0x13 | ||
56 | #define DSI_TX_CRC 0x14 | ||
57 | #define DSI_STATUS 0x15 | ||
58 | #define DSI_STATUS_IDLE (1 << 10) | ||
59 | #define DSI_INIT_SEQ_CONTROL 0x1a | ||
60 | #define DSI_INIT_SEQ_DATA_0 0x1b | ||
61 | #define DSI_INIT_SEQ_DATA_1 0x1c | ||
62 | #define DSI_INIT_SEQ_DATA_2 0x1d | ||
63 | #define DSI_INIT_SEQ_DATA_3 0x1e | ||
64 | #define DSI_INIT_SEQ_DATA_4 0x1f | ||
65 | #define DSI_INIT_SEQ_DATA_5 0x20 | ||
66 | #define DSI_INIT_SEQ_DATA_6 0x21 | ||
67 | #define DSI_INIT_SEQ_DATA_7 0x22 | ||
68 | #define DSI_PKT_SEQ_0_LO 0x23 | ||
69 | #define DSI_PKT_SEQ_0_HI 0x24 | ||
70 | #define DSI_PKT_SEQ_1_LO 0x25 | ||
71 | #define DSI_PKT_SEQ_1_HI 0x26 | ||
72 | #define DSI_PKT_SEQ_2_LO 0x27 | ||
73 | #define DSI_PKT_SEQ_2_HI 0x28 | ||
74 | #define DSI_PKT_SEQ_3_LO 0x29 | ||
75 | #define DSI_PKT_SEQ_3_HI 0x2a | ||
76 | #define DSI_PKT_SEQ_4_LO 0x2b | ||
77 | #define DSI_PKT_SEQ_4_HI 0x2c | ||
78 | #define DSI_PKT_SEQ_5_LO 0x2d | ||
79 | #define DSI_PKT_SEQ_5_HI 0x2e | ||
80 | #define DSI_DCS_CMDS 0x33 | ||
81 | #define DSI_PKT_LEN_0_1 0x34 | ||
82 | #define DSI_PKT_LEN_2_3 0x35 | ||
83 | #define DSI_PKT_LEN_4_5 0x36 | ||
84 | #define DSI_PKT_LEN_6_7 0x37 | ||
85 | #define DSI_PHY_TIMING_0 0x3c | ||
86 | #define DSI_PHY_TIMING_1 0x3d | ||
87 | #define DSI_PHY_TIMING_2 0x3e | ||
88 | #define DSI_BTA_TIMING 0x3f | ||
89 | |||
90 | #define DSI_TIMING_FIELD(value, period, hwinc) \ | ||
91 | ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff) | ||
92 | |||
93 | #define DSI_TIMEOUT_0 0x44 | ||
94 | #define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16) | ||
95 | #define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0) | ||
96 | #define DSI_TIMEOUT_1 0x45 | ||
97 | #define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16) | ||
98 | #define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0) | ||
99 | #define DSI_TO_TALLY 0x46 | ||
100 | #define DSI_TALLY_TA(x) (((x) & 0xff) << 16) | ||
101 | #define DSI_TALLY_LRX(x) (((x) & 0xff) << 8) | ||
102 | #define DSI_TALLY_HTX(x) (((x) & 0xff) << 0) | ||
103 | #define DSI_PAD_CONTROL_0 0x4b | ||
104 | #define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) | ||
105 | #define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8) | ||
106 | #define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) | ||
107 | #define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24) | ||
108 | #define DSI_PAD_CONTROL_CD 0x4c | ||
109 | #define DSI_PAD_CD_STATUS 0x4d | ||
110 | #define DSI_VIDEO_MODE_CONTROL 0x4e | ||
111 | #define DSI_PAD_CONTROL_1 0x4f | ||
112 | #define DSI_PAD_CONTROL_2 0x50 | ||
113 | #define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0) | ||
114 | #define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4) | ||
115 | #define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8) | ||
116 | #define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12) | ||
117 | #define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16) | ||
118 | #define DSI_PAD_CONTROL_3 0x51 | ||
119 | #define DSI_PAD_CONTROL_4 0x52 | ||
120 | #define DSI_GANGED_MODE_CONTROL 0x53 | ||
121 | #define DSI_GANGED_MODE_START 0x54 | ||
122 | #define DSI_GANGED_MODE_SIZE 0x55 | ||
123 | #define DSI_RAW_DATA_BYTE_COUNT 0x56 | ||
124 | #define DSI_ULTRA_LOW_POWER_CONTROL 0x57 | ||
125 | #define DSI_INIT_SEQ_DATA_8 0x58 | ||
126 | #define DSI_INIT_SEQ_DATA_9 0x59 | ||
127 | #define DSI_INIT_SEQ_DATA_10 0x5a | ||
128 | #define DSI_INIT_SEQ_DATA_11 0x5b | ||
129 | #define DSI_INIT_SEQ_DATA_12 0x5c | ||
130 | #define DSI_INIT_SEQ_DATA_13 0x5d | ||
131 | #define DSI_INIT_SEQ_DATA_14 0x5e | ||
132 | #define DSI_INIT_SEQ_DATA_15 0x5f | ||
133 | |||
134 | #endif | ||
diff --git a/drivers/gpu/drm/tegra/mipi-phy.c b/drivers/gpu/drm/tegra/mipi-phy.c new file mode 100644 index 000000000000..1d2ad267cfce --- /dev/null +++ b/drivers/gpu/drm/tegra/mipi-phy.c | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 NVIDIA Corporation | ||
3 | * | ||
4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
5 | * documentation for any purpose is hereby granted without fee, provided that | ||
6 | * the above copyright notice appear in all copies and that both that copyright | ||
7 | * notice and this permission notice appear in supporting documentation, and | ||
8 | * that the name of the copyright holders not be used in advertising or | ||
9 | * publicity pertaining to distribution of the software without specific, | ||
10 | * written prior permission. The copyright holders make no representations | ||
11 | * about the suitability of this software for any purpose. It is provided "as | ||
12 | * is" without express or implied warranty. | ||
13 | * | ||
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
20 | * OF THIS SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | |||
25 | #include "mipi-phy.h" | ||
26 | |||
27 | /* | ||
28 | * Default D-PHY timings based on MIPI D-PHY specification. Derived from | ||
29 | * the valid ranges specified in Section 5.9 of the D-PHY specification | ||
30 | * with minor adjustments. | ||
31 | */ | ||
32 | int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, | ||
33 | unsigned long period) | ||
34 | { | ||
35 | timing->clkmiss = 0; | ||
36 | timing->clkpost = 70 + 52 * period; | ||
37 | timing->clkpre = 8; | ||
38 | timing->clkprepare = 65; | ||
39 | timing->clksettle = 95; | ||
40 | timing->clktermen = 0; | ||
41 | timing->clktrail = 80; | ||
42 | timing->clkzero = 260; | ||
43 | timing->dtermen = 0; | ||
44 | timing->eot = 0; | ||
45 | timing->hsexit = 120; | ||
46 | timing->hsprepare = 65 + 5 * period; | ||
47 | timing->hszero = 145 + 5 * period; | ||
48 | timing->hssettle = 85 + 6 * period; | ||
49 | timing->hsskip = 40; | ||
50 | timing->hstrail = max(8 * period, 60 + 4 * period); | ||
51 | timing->init = 100000; | ||
52 | timing->lpx = 60; | ||
53 | timing->taget = 5 * timing->lpx; | ||
54 | timing->tago = 4 * timing->lpx; | ||
55 | timing->tasure = 2 * timing->lpx; | ||
56 | timing->wakeup = 1000000; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Validate D-PHY timing according to MIPI Alliance Specification for D-PHY, | ||
63 | * Section 5.9 "Global Operation Timing Parameters". | ||
64 | */ | ||
65 | int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, | ||
66 | unsigned long period) | ||
67 | { | ||
68 | if (timing->clkmiss > 60) | ||
69 | return -EINVAL; | ||
70 | |||
71 | if (timing->clkpost < (60 + 52 * period)) | ||
72 | return -EINVAL; | ||
73 | |||
74 | if (timing->clkpre < 8) | ||
75 | return -EINVAL; | ||
76 | |||
77 | if (timing->clkprepare < 38 || timing->clkprepare > 95) | ||
78 | return -EINVAL; | ||
79 | |||
80 | if (timing->clksettle < 95 || timing->clksettle > 300) | ||
81 | return -EINVAL; | ||
82 | |||
83 | if (timing->clktermen > 38) | ||
84 | return -EINVAL; | ||
85 | |||
86 | if (timing->clktrail < 60) | ||
87 | return -EINVAL; | ||
88 | |||
89 | if (timing->clkprepare + timing->clkzero < 300) | ||
90 | return -EINVAL; | ||
91 | |||
92 | if (timing->dtermen > 35 + 4 * period) | ||
93 | return -EINVAL; | ||
94 | |||
95 | if (timing->eot > 105 + 12 * period) | ||
96 | return -EINVAL; | ||
97 | |||
98 | if (timing->hsexit < 100) | ||
99 | return -EINVAL; | ||
100 | |||
101 | if (timing->hsprepare < 40 + 4 * period || | ||
102 | timing->hsprepare > 85 + 6 * period) | ||
103 | return -EINVAL; | ||
104 | |||
105 | if (timing->hsprepare + timing->hszero < 145 + 10 * period) | ||
106 | return -EINVAL; | ||
107 | |||
108 | if ((timing->hssettle < 85 + 6 * period) || | ||
109 | (timing->hssettle > 145 + 10 * period)) | ||
110 | return -EINVAL; | ||
111 | |||
112 | if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period) | ||
113 | return -EINVAL; | ||
114 | |||
115 | if (timing->hstrail < max(8 * period, 60 + 4 * period)) | ||
116 | return -EINVAL; | ||
117 | |||
118 | if (timing->init < 100000) | ||
119 | return -EINVAL; | ||
120 | |||
121 | if (timing->lpx < 50) | ||
122 | return -EINVAL; | ||
123 | |||
124 | if (timing->taget != 5 * timing->lpx) | ||
125 | return -EINVAL; | ||
126 | |||
127 | if (timing->tago != 4 * timing->lpx) | ||
128 | return -EINVAL; | ||
129 | |||
130 | if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx) | ||
131 | return -EINVAL; | ||
132 | |||
133 | if (timing->wakeup < 1000000) | ||
134 | return -EINVAL; | ||
135 | |||
136 | return 0; | ||
137 | } | ||
diff --git a/drivers/gpu/drm/tegra/mipi-phy.h b/drivers/gpu/drm/tegra/mipi-phy.h new file mode 100644 index 000000000000..d3591694432d --- /dev/null +++ b/drivers/gpu/drm/tegra/mipi-phy.h | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 NVIDIA Corporation | ||
3 | * | ||
4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
5 | * documentation for any purpose is hereby granted without fee, provided that | ||
6 | * the above copyright notice appear in all copies and that both that copyright | ||
7 | * notice and this permission notice appear in supporting documentation, and | ||
8 | * that the name of the copyright holders not be used in advertising or | ||
9 | * publicity pertaining to distribution of the software without specific, | ||
10 | * written prior permission. The copyright holders make no representations | ||
11 | * about the suitability of this software for any purpose. It is provided "as | ||
12 | * is" without express or implied warranty. | ||
13 | * | ||
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | ||
20 | * OF THIS SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #ifndef DRM_TEGRA_MIPI_PHY_H | ||
24 | #define DRM_TEGRA_MIPI_PHY_H | ||
25 | |||
26 | /* | ||
27 | * D-PHY timing parameters | ||
28 | * | ||
29 | * A detailed description of these parameters can be found in the MIPI | ||
30 | * Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing | ||
31 | * Parameters". | ||
32 | * | ||
33 | * All parameters are specified in nanoseconds. | ||
34 | */ | ||
35 | struct mipi_dphy_timing { | ||
36 | unsigned int clkmiss; | ||
37 | unsigned int clkpost; | ||
38 | unsigned int clkpre; | ||
39 | unsigned int clkprepare; | ||
40 | unsigned int clksettle; | ||
41 | unsigned int clktermen; | ||
42 | unsigned int clktrail; | ||
43 | unsigned int clkzero; | ||
44 | unsigned int dtermen; | ||
45 | unsigned int eot; | ||
46 | unsigned int hsexit; | ||
47 | unsigned int hsprepare; | ||
48 | unsigned int hszero; | ||
49 | unsigned int hssettle; | ||
50 | unsigned int hsskip; | ||
51 | unsigned int hstrail; | ||
52 | unsigned int init; | ||
53 | unsigned int lpx; | ||
54 | unsigned int taget; | ||
55 | unsigned int tago; | ||
56 | unsigned int tasure; | ||
57 | unsigned int wakeup; | ||
58 | }; | ||
59 | |||
60 | int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, | ||
61 | unsigned long period); | ||
62 | int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, | ||
63 | unsigned long period); | ||
64 | |||
65 | #endif | ||
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 581dc5d37bed..f1b5030f55e3 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c | |||
@@ -284,6 +284,11 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) | |||
284 | encoder = DRM_MODE_ENCODER_TMDS; | 284 | encoder = DRM_MODE_ENCODER_TMDS; |
285 | break; | 285 | break; |
286 | 286 | ||
287 | case TEGRA_OUTPUT_DSI: | ||
288 | connector = DRM_MODE_CONNECTOR_DSI; | ||
289 | encoder = DRM_MODE_ENCODER_DSI; | ||
290 | break; | ||
291 | |||
287 | default: | 292 | default: |
288 | connector = DRM_MODE_CONNECTOR_Unknown; | 293 | connector = DRM_MODE_CONNECTOR_Unknown; |
289 | encoder = DRM_MODE_ENCODER_NONE; | 294 | encoder = DRM_MODE_ENCODER_NONE; |