diff options
-rw-r--r-- | Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt | 21 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/Makefile | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 602 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.c | 605 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.h | 150 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_regs.h | 154 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 419 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_tfp410.h | 26 |
12 files changed, 2019 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt b/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt new file mode 100644 index 000000000000..a58ae7756fc6 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt | |||
@@ -0,0 +1,21 @@ | |||
1 | Device-Tree bindings for tilcdc DRM TFP410 output driver | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: value should be "ti,tilcdc,tfp410". | ||
5 | - i2c: the phandle for the i2c device to use for DDC | ||
6 | |||
7 | Recommended properties: | ||
8 | - pinctrl-names, pinctrl-0: the pincontrol settings to configure | ||
9 | muxing properly for pins that connect to TFP410 device | ||
10 | - powerdn-gpio: the powerdown GPIO, pulled low to power down the | ||
11 | TFP410 device (for DPMS_OFF) | ||
12 | |||
13 | Example: | ||
14 | |||
15 | dvicape { | ||
16 | compatible = "ti,tilcdc,tfp410"; | ||
17 | i2c = <&i2c2>; | ||
18 | pinctrl-names = "default"; | ||
19 | pinctrl-0 = <&bone_dvi_cape_dvi_00A1_pins>; | ||
20 | powerdn-gpio = <&gpio2 31 0>; | ||
21 | }; | ||
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt new file mode 100644 index 000000000000..e5f130159ae1 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt | |||
@@ -0,0 +1,21 @@ | |||
1 | Device-Tree bindings for tilcdc DRM driver | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: value should be "ti,am33xx-tilcdc". | ||
5 | - interrupts: the interrupt number | ||
6 | - reg: base address and size of the LCDC device | ||
7 | |||
8 | Recommended properties: | ||
9 | - interrupt-parent: the phandle for the interrupt controller that | ||
10 | services interrupts for this device. | ||
11 | - ti,hwmods: Name of the hwmod associated to the LCDC | ||
12 | |||
13 | Example: | ||
14 | |||
15 | fb: fb@4830e000 { | ||
16 | compatible = "ti,am33xx-tilcdc"; | ||
17 | reg = <0x4830e000 0x1000>; | ||
18 | interrupt-parent = <&intc>; | ||
19 | interrupts = <36>; | ||
20 | ti,hwmods = "lcdc"; | ||
21 | }; | ||
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed9e3af17b31..ba74e75f7d54 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
@@ -215,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig" | |||
215 | source "drivers/gpu/drm/shmobile/Kconfig" | 215 | source "drivers/gpu/drm/shmobile/Kconfig" |
216 | 216 | ||
217 | source "drivers/gpu/drm/tegra/Kconfig" | 217 | source "drivers/gpu/drm/tegra/Kconfig" |
218 | |||
219 | source "drivers/gpu/drm/tilcdc/Kconfig" | ||
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81cfcbc..3af934dffe68 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/ | |||
50 | obj-$(CONFIG_DRM_AST) += ast/ | 50 | obj-$(CONFIG_DRM_AST) += ast/ |
51 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | 51 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
52 | obj-$(CONFIG_DRM_TEGRA) += tegra/ | 52 | obj-$(CONFIG_DRM_TEGRA) += tegra/ |
53 | obj-$(CONFIG_DRM_TILCDC) += tilcdc/ | ||
53 | obj-y += i2c/ | 54 | obj-y += i2c/ |
diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig new file mode 100644 index 000000000000..ee9b592d59eb --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | config DRM_TILCDC | ||
2 | tristate "DRM Support for TI LCDC Display Controller" | ||
3 | depends on DRM && OF | ||
4 | select DRM_KMS_HELPER | ||
5 | select DRM_KMS_CMA_HELPER | ||
6 | select DRM_GEM_CMA_HELPER | ||
7 | help | ||
8 | Choose this option if you have an TI SoC with LCDC display | ||
9 | controller, for example AM33xx in beagle-bone, DA8xx, or | ||
10 | OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. | ||
diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile new file mode 100644 index 000000000000..1359cc29cd81 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | ccflags-y := -Iinclude/drm -Werror | ||
2 | |||
3 | tilcdc-y := \ | ||
4 | tilcdc_crtc.o \ | ||
5 | tilcdc_tfp410.o \ | ||
6 | tilcdc_drv.o | ||
7 | |||
8 | obj-$(CONFIG_DRM_TILCDC) += tilcdc.o | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c new file mode 100644 index 000000000000..5dd3c7d031d5 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <linux/kfifo.h> | ||
19 | |||
20 | #include "tilcdc_drv.h" | ||
21 | #include "tilcdc_regs.h" | ||
22 | |||
23 | struct tilcdc_crtc { | ||
24 | struct drm_crtc base; | ||
25 | |||
26 | const struct tilcdc_panel_info *info; | ||
27 | uint32_t dirty; | ||
28 | dma_addr_t start, end; | ||
29 | struct drm_pending_vblank_event *event; | ||
30 | int dpms; | ||
31 | wait_queue_head_t frame_done_wq; | ||
32 | bool frame_done; | ||
33 | |||
34 | /* fb currently set to scanout 0/1: */ | ||
35 | struct drm_framebuffer *scanout[2]; | ||
36 | |||
37 | /* for deferred fb unref's: */ | ||
38 | DECLARE_KFIFO_PTR(unref_fifo, struct drm_framebuffer *); | ||
39 | struct work_struct work; | ||
40 | }; | ||
41 | #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) | ||
42 | |||
43 | static void unref_worker(struct work_struct *work) | ||
44 | { | ||
45 | struct tilcdc_crtc *tilcdc_crtc = container_of(work, struct tilcdc_crtc, work); | ||
46 | struct drm_device *dev = tilcdc_crtc->base.dev; | ||
47 | struct drm_framebuffer *fb; | ||
48 | |||
49 | mutex_lock(&dev->mode_config.mutex); | ||
50 | while (kfifo_get(&tilcdc_crtc->unref_fifo, &fb)) | ||
51 | drm_framebuffer_unreference(fb); | ||
52 | mutex_unlock(&dev->mode_config.mutex); | ||
53 | } | ||
54 | |||
55 | static void set_scanout(struct drm_crtc *crtc, int n) | ||
56 | { | ||
57 | static const uint32_t base_reg[] = { | ||
58 | LCDC_DMA_FB_BASE_ADDR_0_REG, LCDC_DMA_FB_BASE_ADDR_1_REG, | ||
59 | }; | ||
60 | static const uint32_t ceil_reg[] = { | ||
61 | LCDC_DMA_FB_CEILING_ADDR_0_REG, LCDC_DMA_FB_CEILING_ADDR_1_REG, | ||
62 | }; | ||
63 | static const uint32_t stat[] = { | ||
64 | LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, | ||
65 | }; | ||
66 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
67 | struct drm_device *dev = crtc->dev; | ||
68 | |||
69 | pm_runtime_get_sync(dev->dev); | ||
70 | tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); | ||
71 | tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); | ||
72 | if (tilcdc_crtc->scanout[n]) { | ||
73 | if (kfifo_put(&tilcdc_crtc->unref_fifo, | ||
74 | (const struct drm_framebuffer **)&tilcdc_crtc->scanout[n])) { | ||
75 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
76 | queue_work(priv->wq, &tilcdc_crtc->work); | ||
77 | } else { | ||
78 | dev_err(dev->dev, "unref fifo full!\n"); | ||
79 | drm_framebuffer_unreference(tilcdc_crtc->scanout[n]); | ||
80 | } | ||
81 | } | ||
82 | tilcdc_crtc->scanout[n] = crtc->fb; | ||
83 | drm_framebuffer_reference(tilcdc_crtc->scanout[n]); | ||
84 | tilcdc_crtc->dirty &= ~stat[n]; | ||
85 | pm_runtime_put_sync(dev->dev); | ||
86 | } | ||
87 | |||
88 | static void update_scanout(struct drm_crtc *crtc) | ||
89 | { | ||
90 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
91 | struct drm_device *dev = crtc->dev; | ||
92 | struct drm_framebuffer *fb = crtc->fb; | ||
93 | struct drm_gem_cma_object *gem; | ||
94 | unsigned int depth, bpp; | ||
95 | |||
96 | drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); | ||
97 | gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
98 | |||
99 | tilcdc_crtc->start = gem->paddr + fb->offsets[0] + | ||
100 | (crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); | ||
101 | |||
102 | tilcdc_crtc->end = tilcdc_crtc->start + | ||
103 | (crtc->mode.vdisplay * fb->pitches[0]); | ||
104 | |||
105 | if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { | ||
106 | /* already enabled, so just mark the frames that need | ||
107 | * updating and they will be updated on vblank: | ||
108 | */ | ||
109 | tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; | ||
110 | drm_vblank_get(dev, 0); | ||
111 | } else { | ||
112 | /* not enabled yet, so update registers immediately: */ | ||
113 | set_scanout(crtc, 0); | ||
114 | set_scanout(crtc, 1); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static void start(struct drm_crtc *crtc) | ||
119 | { | ||
120 | struct drm_device *dev = crtc->dev; | ||
121 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
122 | |||
123 | if (priv->rev == 2) { | ||
124 | tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | ||
125 | msleep(1); | ||
126 | tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | ||
127 | msleep(1); | ||
128 | } | ||
129 | |||
130 | tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); | ||
131 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); | ||
132 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | ||
133 | } | ||
134 | |||
135 | static void stop(struct drm_crtc *crtc) | ||
136 | { | ||
137 | struct drm_device *dev = crtc->dev; | ||
138 | |||
139 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | ||
140 | } | ||
141 | |||
142 | static void tilcdc_crtc_destroy(struct drm_crtc *crtc) | ||
143 | { | ||
144 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
145 | |||
146 | WARN_ON(tilcdc_crtc->dpms == DRM_MODE_DPMS_ON); | ||
147 | |||
148 | drm_crtc_cleanup(crtc); | ||
149 | WARN_ON(!kfifo_is_empty(&tilcdc_crtc->unref_fifo)); | ||
150 | kfifo_free(&tilcdc_crtc->unref_fifo); | ||
151 | kfree(tilcdc_crtc); | ||
152 | } | ||
153 | |||
154 | static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, | ||
155 | struct drm_framebuffer *fb, | ||
156 | struct drm_pending_vblank_event *event) | ||
157 | { | ||
158 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
159 | struct drm_device *dev = crtc->dev; | ||
160 | |||
161 | if (tilcdc_crtc->event) { | ||
162 | dev_err(dev->dev, "already pending page flip!\n"); | ||
163 | return -EBUSY; | ||
164 | } | ||
165 | |||
166 | crtc->fb = fb; | ||
167 | tilcdc_crtc->event = event; | ||
168 | update_scanout(crtc); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
174 | { | ||
175 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
176 | struct drm_device *dev = crtc->dev; | ||
177 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
178 | |||
179 | /* we really only care about on or off: */ | ||
180 | if (mode != DRM_MODE_DPMS_ON) | ||
181 | mode = DRM_MODE_DPMS_OFF; | ||
182 | |||
183 | if (tilcdc_crtc->dpms == mode) | ||
184 | return; | ||
185 | |||
186 | tilcdc_crtc->dpms = mode; | ||
187 | |||
188 | pm_runtime_get_sync(dev->dev); | ||
189 | |||
190 | if (mode == DRM_MODE_DPMS_ON) { | ||
191 | pm_runtime_forbid(dev->dev); | ||
192 | start(crtc); | ||
193 | } else { | ||
194 | tilcdc_crtc->frame_done = false; | ||
195 | stop(crtc); | ||
196 | |||
197 | /* if necessary wait for framedone irq which will still come | ||
198 | * before putting things to sleep.. | ||
199 | */ | ||
200 | if (priv->rev == 2) { | ||
201 | int ret = wait_event_timeout( | ||
202 | tilcdc_crtc->frame_done_wq, | ||
203 | tilcdc_crtc->frame_done, | ||
204 | msecs_to_jiffies(50)); | ||
205 | if (ret == 0) | ||
206 | dev_err(dev->dev, "timeout waiting for framedone\n"); | ||
207 | } | ||
208 | pm_runtime_allow(dev->dev); | ||
209 | } | ||
210 | |||
211 | pm_runtime_put_sync(dev->dev); | ||
212 | } | ||
213 | |||
214 | static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, | ||
215 | const struct drm_display_mode *mode, | ||
216 | struct drm_display_mode *adjusted_mode) | ||
217 | { | ||
218 | return true; | ||
219 | } | ||
220 | |||
221 | static void tilcdc_crtc_prepare(struct drm_crtc *crtc) | ||
222 | { | ||
223 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
224 | } | ||
225 | |||
226 | static void tilcdc_crtc_commit(struct drm_crtc *crtc) | ||
227 | { | ||
228 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
229 | } | ||
230 | |||
231 | static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, | ||
232 | struct drm_display_mode *mode, | ||
233 | struct drm_display_mode *adjusted_mode, | ||
234 | int x, int y, | ||
235 | struct drm_framebuffer *old_fb) | ||
236 | { | ||
237 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
238 | struct drm_device *dev = crtc->dev; | ||
239 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
240 | const struct tilcdc_panel_info *info = tilcdc_crtc->info; | ||
241 | uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw; | ||
242 | int ret; | ||
243 | |||
244 | ret = tilcdc_crtc_mode_valid(crtc, mode); | ||
245 | if (WARN_ON(ret)) | ||
246 | return ret; | ||
247 | |||
248 | if (WARN_ON(!info)) | ||
249 | return -EINVAL; | ||
250 | |||
251 | pm_runtime_get_sync(dev->dev); | ||
252 | |||
253 | /* Configure the Burst Size and fifo threshold of DMA: */ | ||
254 | reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770; | ||
255 | switch (info->dma_burst_sz) { | ||
256 | case 1: | ||
257 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_1); | ||
258 | break; | ||
259 | case 2: | ||
260 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_2); | ||
261 | break; | ||
262 | case 4: | ||
263 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_4); | ||
264 | break; | ||
265 | case 8: | ||
266 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_8); | ||
267 | break; | ||
268 | case 16: | ||
269 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16); | ||
270 | break; | ||
271 | default: | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | reg |= (info->fifo_th << 8); | ||
275 | tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg); | ||
276 | |||
277 | /* Configure timings: */ | ||
278 | hbp = mode->htotal - mode->hsync_end; | ||
279 | hfp = mode->hsync_start - mode->hdisplay; | ||
280 | hsw = mode->hsync_end - mode->hsync_start; | ||
281 | vbp = mode->vtotal - mode->vsync_end; | ||
282 | vfp = mode->vsync_start - mode->vdisplay; | ||
283 | vsw = mode->vsync_end - mode->vsync_start; | ||
284 | |||
285 | DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u", | ||
286 | mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); | ||
287 | |||
288 | /* Configure the AC Bias Period and Number of Transitions per Interrupt: */ | ||
289 | reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00; | ||
290 | reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) | | ||
291 | LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt); | ||
292 | if (priv->rev == 2) { | ||
293 | reg |= (hfp & 0x300) >> 8; | ||
294 | reg |= (hbp & 0x300) >> 4; | ||
295 | reg |= (hsw & 0x3c0) << 21; | ||
296 | } | ||
297 | tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg); | ||
298 | |||
299 | reg = (((mode->hdisplay >> 4) - 1) << 4) | | ||
300 | ((hbp & 0xff) << 24) | | ||
301 | ((hfp & 0xff) << 16) | | ||
302 | ((hsw & 0x3f) << 10); | ||
303 | if (priv->rev == 2) | ||
304 | reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3; | ||
305 | tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg); | ||
306 | |||
307 | reg = ((mode->vdisplay - 1) & 0x3ff) | | ||
308 | ((vbp & 0xff) << 24) | | ||
309 | ((vfp & 0xff) << 16) | | ||
310 | ((vsw & 0x3f) << 10); | ||
311 | tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg); | ||
312 | |||
313 | /* Configure display type: */ | ||
314 | reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) & | ||
315 | ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE | | ||
316 | LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000); | ||
317 | reg |= LCDC_TFT_MODE; /* no monochrome/passive support */ | ||
318 | if (info->tft_alt_mode) | ||
319 | reg |= LCDC_TFT_ALT_ENABLE; | ||
320 | if (priv->rev == 2) { | ||
321 | unsigned int depth, bpp; | ||
322 | |||
323 | drm_fb_get_bpp_depth(crtc->fb->pixel_format, &depth, &bpp); | ||
324 | switch (bpp) { | ||
325 | case 16: | ||
326 | break; | ||
327 | case 32: | ||
328 | reg |= LCDC_V2_TFT_24BPP_UNPACK; | ||
329 | /* fallthrough */ | ||
330 | case 24: | ||
331 | reg |= LCDC_V2_TFT_24BPP_MODE; | ||
332 | break; | ||
333 | default: | ||
334 | dev_err(dev->dev, "invalid pixel format\n"); | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | } | ||
338 | reg |= info->fdd < 12; | ||
339 | tilcdc_write(dev, LCDC_RASTER_CTRL_REG, reg); | ||
340 | |||
341 | if (info->invert_pxl_clk) | ||
342 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | ||
343 | else | ||
344 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | ||
345 | |||
346 | if (info->sync_ctrl) | ||
347 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | ||
348 | else | ||
349 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | ||
350 | |||
351 | if (info->sync_edge) | ||
352 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | ||
353 | else | ||
354 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | ||
355 | |||
356 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
357 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | ||
358 | else | ||
359 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | ||
360 | |||
361 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
362 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | ||
363 | else | ||
364 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | ||
365 | |||
366 | if (info->raster_order) | ||
367 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | ||
368 | else | ||
369 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | ||
370 | |||
371 | |||
372 | update_scanout(crtc); | ||
373 | tilcdc_crtc_update_clk(crtc); | ||
374 | |||
375 | pm_runtime_put_sync(dev->dev); | ||
376 | |||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
381 | struct drm_framebuffer *old_fb) | ||
382 | { | ||
383 | update_scanout(crtc); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static void tilcdc_crtc_load_lut(struct drm_crtc *crtc) | ||
388 | { | ||
389 | } | ||
390 | |||
391 | static const struct drm_crtc_funcs tilcdc_crtc_funcs = { | ||
392 | .destroy = tilcdc_crtc_destroy, | ||
393 | .set_config = drm_crtc_helper_set_config, | ||
394 | .page_flip = tilcdc_crtc_page_flip, | ||
395 | }; | ||
396 | |||
397 | static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { | ||
398 | .dpms = tilcdc_crtc_dpms, | ||
399 | .mode_fixup = tilcdc_crtc_mode_fixup, | ||
400 | .prepare = tilcdc_crtc_prepare, | ||
401 | .commit = tilcdc_crtc_commit, | ||
402 | .mode_set = tilcdc_crtc_mode_set, | ||
403 | .mode_set_base = tilcdc_crtc_mode_set_base, | ||
404 | .load_lut = tilcdc_crtc_load_lut, | ||
405 | }; | ||
406 | |||
407 | int tilcdc_crtc_max_width(struct drm_crtc *crtc) | ||
408 | { | ||
409 | struct drm_device *dev = crtc->dev; | ||
410 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
411 | int max_width = 0; | ||
412 | |||
413 | if (priv->rev == 1) | ||
414 | max_width = 1024; | ||
415 | else if (priv->rev == 2) | ||
416 | max_width = 2048; | ||
417 | |||
418 | return max_width; | ||
419 | } | ||
420 | |||
421 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode) | ||
422 | { | ||
423 | struct tilcdc_drm_private *priv = crtc->dev->dev_private; | ||
424 | unsigned int bandwidth; | ||
425 | |||
426 | if (mode->hdisplay > tilcdc_crtc_max_width(crtc)) | ||
427 | return MODE_VIRTUAL_X; | ||
428 | |||
429 | /* width must be multiple of 16 */ | ||
430 | if (mode->hdisplay & 0xf) | ||
431 | return MODE_VIRTUAL_X; | ||
432 | |||
433 | if (mode->vdisplay > 2048) | ||
434 | return MODE_VIRTUAL_Y; | ||
435 | |||
436 | /* filter out modes that would require too much memory bandwidth: */ | ||
437 | bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode); | ||
438 | if (bandwidth > priv->max_bandwidth) | ||
439 | return MODE_BAD; | ||
440 | |||
441 | return MODE_OK; | ||
442 | } | ||
443 | |||
444 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | ||
445 | const struct tilcdc_panel_info *info) | ||
446 | { | ||
447 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
448 | tilcdc_crtc->info = info; | ||
449 | } | ||
450 | |||
451 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc) | ||
452 | { | ||
453 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
454 | struct drm_device *dev = crtc->dev; | ||
455 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
456 | int dpms = tilcdc_crtc->dpms; | ||
457 | unsigned int lcd_clk, div; | ||
458 | int ret; | ||
459 | |||
460 | pm_runtime_get_sync(dev->dev); | ||
461 | |||
462 | if (dpms == DRM_MODE_DPMS_ON) | ||
463 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
464 | |||
465 | /* in raster mode, minimum divisor is 2: */ | ||
466 | ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); | ||
467 | if (ret) { | ||
468 | dev_err(dev->dev, "failed to set display clock rate to: %d\n", | ||
469 | crtc->mode.clock); | ||
470 | goto out; | ||
471 | } | ||
472 | |||
473 | lcd_clk = clk_get_rate(priv->clk); | ||
474 | div = lcd_clk / (crtc->mode.clock * 1000); | ||
475 | |||
476 | DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); | ||
477 | DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); | ||
478 | |||
479 | /* Configure the LCD clock divisor. */ | ||
480 | tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | | ||
481 | LCDC_RASTER_MODE); | ||
482 | |||
483 | if (priv->rev == 2) | ||
484 | tilcdc_set(dev, LCDC_CLK_ENABLE_REG, | ||
485 | LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | | ||
486 | LCDC_V2_CORE_CLK_EN); | ||
487 | |||
488 | if (dpms == DRM_MODE_DPMS_ON) | ||
489 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
490 | |||
491 | out: | ||
492 | pm_runtime_put_sync(dev->dev); | ||
493 | } | ||
494 | |||
495 | irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) | ||
496 | { | ||
497 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
498 | struct drm_device *dev = crtc->dev; | ||
499 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
500 | uint32_t stat = tilcdc_read_irqstatus(dev); | ||
501 | |||
502 | if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { | ||
503 | stop(crtc); | ||
504 | dev_err(dev->dev, "error: %08x\n", stat); | ||
505 | tilcdc_clear_irqstatus(dev, stat); | ||
506 | start(crtc); | ||
507 | } else if (stat & LCDC_PL_LOAD_DONE) { | ||
508 | tilcdc_clear_irqstatus(dev, stat); | ||
509 | } else { | ||
510 | struct drm_pending_vblank_event *event; | ||
511 | unsigned long flags; | ||
512 | uint32_t dirty = tilcdc_crtc->dirty & stat; | ||
513 | |||
514 | tilcdc_clear_irqstatus(dev, stat); | ||
515 | |||
516 | if (dirty & LCDC_END_OF_FRAME0) | ||
517 | set_scanout(crtc, 0); | ||
518 | |||
519 | if (dirty & LCDC_END_OF_FRAME1) | ||
520 | set_scanout(crtc, 1); | ||
521 | |||
522 | drm_handle_vblank(dev, 0); | ||
523 | |||
524 | spin_lock_irqsave(&dev->event_lock, flags); | ||
525 | event = tilcdc_crtc->event; | ||
526 | tilcdc_crtc->event = NULL; | ||
527 | if (event) | ||
528 | drm_send_vblank_event(dev, 0, event); | ||
529 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
530 | |||
531 | if (dirty && !tilcdc_crtc->dirty) | ||
532 | drm_vblank_put(dev, 0); | ||
533 | } | ||
534 | |||
535 | if (priv->rev == 2) { | ||
536 | if (stat & LCDC_FRAME_DONE) { | ||
537 | tilcdc_crtc->frame_done = true; | ||
538 | wake_up(&tilcdc_crtc->frame_done_wq); | ||
539 | } | ||
540 | tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); | ||
541 | } | ||
542 | |||
543 | return IRQ_HANDLED; | ||
544 | } | ||
545 | |||
546 | void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) | ||
547 | { | ||
548 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
549 | struct drm_pending_vblank_event *event; | ||
550 | struct drm_device *dev = crtc->dev; | ||
551 | unsigned long flags; | ||
552 | |||
553 | /* Destroy the pending vertical blanking event associated with the | ||
554 | * pending page flip, if any, and disable vertical blanking interrupts. | ||
555 | */ | ||
556 | spin_lock_irqsave(&dev->event_lock, flags); | ||
557 | event = tilcdc_crtc->event; | ||
558 | if (event && event->base.file_priv == file) { | ||
559 | tilcdc_crtc->event = NULL; | ||
560 | event->base.destroy(&event->base); | ||
561 | drm_vblank_put(dev, 0); | ||
562 | } | ||
563 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
564 | } | ||
565 | |||
566 | struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) | ||
567 | { | ||
568 | struct tilcdc_crtc *tilcdc_crtc; | ||
569 | struct drm_crtc *crtc; | ||
570 | int ret; | ||
571 | |||
572 | tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); | ||
573 | if (!tilcdc_crtc) { | ||
574 | dev_err(dev->dev, "allocation failed\n"); | ||
575 | return NULL; | ||
576 | } | ||
577 | |||
578 | crtc = &tilcdc_crtc->base; | ||
579 | |||
580 | tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF; | ||
581 | init_waitqueue_head(&tilcdc_crtc->frame_done_wq); | ||
582 | |||
583 | ret = kfifo_alloc(&tilcdc_crtc->unref_fifo, 16, GFP_KERNEL); | ||
584 | if (ret) { | ||
585 | dev_err(dev->dev, "could not allocate unref FIFO\n"); | ||
586 | goto fail; | ||
587 | } | ||
588 | |||
589 | INIT_WORK(&tilcdc_crtc->work, unref_worker); | ||
590 | |||
591 | ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); | ||
592 | if (ret < 0) | ||
593 | goto fail; | ||
594 | |||
595 | drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); | ||
596 | |||
597 | return crtc; | ||
598 | |||
599 | fail: | ||
600 | tilcdc_crtc_destroy(crtc); | ||
601 | return NULL; | ||
602 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c new file mode 100644 index 000000000000..f6defbfaaffb --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | /* LCDC DRM driver, based on da8xx-fb */ | ||
19 | |||
20 | #include "tilcdc_drv.h" | ||
21 | #include "tilcdc_regs.h" | ||
22 | #include "tilcdc_tfp410.h" | ||
23 | |||
24 | #include "drm_fb_helper.h" | ||
25 | |||
26 | static LIST_HEAD(module_list); | ||
27 | |||
28 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, | ||
29 | const struct tilcdc_module_ops *funcs) | ||
30 | { | ||
31 | mod->name = name; | ||
32 | mod->funcs = funcs; | ||
33 | INIT_LIST_HEAD(&mod->list); | ||
34 | list_add(&mod->list, &module_list); | ||
35 | } | ||
36 | |||
37 | void tilcdc_module_cleanup(struct tilcdc_module *mod) | ||
38 | { | ||
39 | list_del(&mod->list); | ||
40 | } | ||
41 | |||
42 | static struct of_device_id tilcdc_of_match[]; | ||
43 | |||
44 | static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, | ||
45 | struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) | ||
46 | { | ||
47 | return drm_fb_cma_create(dev, file_priv, mode_cmd); | ||
48 | } | ||
49 | |||
50 | static void tilcdc_fb_output_poll_changed(struct drm_device *dev) | ||
51 | { | ||
52 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
53 | if (priv->fbdev) | ||
54 | drm_fbdev_cma_hotplug_event(priv->fbdev); | ||
55 | } | ||
56 | |||
57 | static const struct drm_mode_config_funcs mode_config_funcs = { | ||
58 | .fb_create = tilcdc_fb_create, | ||
59 | .output_poll_changed = tilcdc_fb_output_poll_changed, | ||
60 | }; | ||
61 | |||
62 | static int modeset_init(struct drm_device *dev) | ||
63 | { | ||
64 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
65 | struct tilcdc_module *mod; | ||
66 | |||
67 | drm_mode_config_init(dev); | ||
68 | |||
69 | priv->crtc = tilcdc_crtc_create(dev); | ||
70 | |||
71 | list_for_each_entry(mod, &module_list, list) { | ||
72 | DBG("loading module: %s", mod->name); | ||
73 | mod->funcs->modeset_init(mod, dev); | ||
74 | } | ||
75 | |||
76 | if ((priv->num_encoders = 0) || (priv->num_connectors == 0)) { | ||
77 | /* oh nos! */ | ||
78 | dev_err(dev->dev, "no encoders/connectors found\n"); | ||
79 | return -ENXIO; | ||
80 | } | ||
81 | |||
82 | dev->mode_config.min_width = 0; | ||
83 | dev->mode_config.min_height = 0; | ||
84 | dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); | ||
85 | dev->mode_config.max_height = 2048; | ||
86 | dev->mode_config.funcs = &mode_config_funcs; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | #ifdef CONFIG_CPU_FREQ | ||
92 | static int cpufreq_transition(struct notifier_block *nb, | ||
93 | unsigned long val, void *data) | ||
94 | { | ||
95 | struct tilcdc_drm_private *priv = container_of(nb, | ||
96 | struct tilcdc_drm_private, freq_transition); | ||
97 | if (val == CPUFREQ_POSTCHANGE) { | ||
98 | if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) { | ||
99 | priv->lcd_fck_rate = clk_get_rate(priv->clk); | ||
100 | tilcdc_crtc_update_clk(priv->crtc); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | /* | ||
109 | * DRM operations: | ||
110 | */ | ||
111 | |||
112 | static int tilcdc_unload(struct drm_device *dev) | ||
113 | { | ||
114 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
115 | struct tilcdc_module *mod, *cur; | ||
116 | |||
117 | drm_kms_helper_poll_fini(dev); | ||
118 | drm_mode_config_cleanup(dev); | ||
119 | drm_vblank_cleanup(dev); | ||
120 | |||
121 | pm_runtime_get_sync(dev->dev); | ||
122 | drm_irq_uninstall(dev); | ||
123 | pm_runtime_put_sync(dev->dev); | ||
124 | |||
125 | #ifdef CONFIG_CPU_FREQ | ||
126 | cpufreq_unregister_notifier(&priv->freq_transition, | ||
127 | CPUFREQ_TRANSITION_NOTIFIER); | ||
128 | #endif | ||
129 | |||
130 | if (priv->clk) | ||
131 | clk_put(priv->clk); | ||
132 | |||
133 | if (priv->mmio) | ||
134 | iounmap(priv->mmio); | ||
135 | |||
136 | flush_workqueue(priv->wq); | ||
137 | destroy_workqueue(priv->wq); | ||
138 | |||
139 | dev->dev_private = NULL; | ||
140 | |||
141 | pm_runtime_disable(dev->dev); | ||
142 | |||
143 | list_for_each_entry_safe(mod, cur, &module_list, list) { | ||
144 | DBG("destroying module: %s", mod->name); | ||
145 | mod->funcs->destroy(mod); | ||
146 | } | ||
147 | |||
148 | kfree(priv); | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int tilcdc_load(struct drm_device *dev, unsigned long flags) | ||
154 | { | ||
155 | struct platform_device *pdev = dev->platformdev; | ||
156 | struct device_node *node = pdev->dev.of_node; | ||
157 | struct tilcdc_drm_private *priv; | ||
158 | struct resource *res; | ||
159 | int ret; | ||
160 | |||
161 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
162 | if (!priv) { | ||
163 | dev_err(dev->dev, "failed to allocate private data\n"); | ||
164 | return -ENOMEM; | ||
165 | } | ||
166 | |||
167 | dev->dev_private = priv; | ||
168 | |||
169 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); | ||
170 | |||
171 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
172 | if (!res) { | ||
173 | dev_err(dev->dev, "failed to get memory resource\n"); | ||
174 | ret = -EINVAL; | ||
175 | goto fail; | ||
176 | } | ||
177 | |||
178 | priv->mmio = ioremap_nocache(res->start, resource_size(res)); | ||
179 | if (!priv->mmio) { | ||
180 | dev_err(dev->dev, "failed to ioremap\n"); | ||
181 | ret = -ENOMEM; | ||
182 | goto fail; | ||
183 | } | ||
184 | |||
185 | priv->clk = clk_get(dev->dev, "fck"); | ||
186 | if (IS_ERR(priv->clk)) { | ||
187 | dev_err(dev->dev, "failed to get functional clock\n"); | ||
188 | ret = -ENODEV; | ||
189 | goto fail; | ||
190 | } | ||
191 | |||
192 | priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); | ||
193 | if (IS_ERR(priv->clk)) { | ||
194 | dev_err(dev->dev, "failed to get display clock\n"); | ||
195 | ret = -ENODEV; | ||
196 | goto fail; | ||
197 | } | ||
198 | |||
199 | #ifdef CONFIG_CPU_FREQ | ||
200 | priv->lcd_fck_rate = clk_get_rate(priv->clk); | ||
201 | priv->freq_transition.notifier_call = cpufreq_transition; | ||
202 | ret = cpufreq_register_notifier(&priv->freq_transition, | ||
203 | CPUFREQ_TRANSITION_NOTIFIER); | ||
204 | if (ret) { | ||
205 | dev_err(dev->dev, "failed to register cpufreq notifier\n"); | ||
206 | goto fail; | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth)) | ||
211 | priv->max_bandwidth = 1280 * 1024 * 60; | ||
212 | |||
213 | pm_runtime_enable(dev->dev); | ||
214 | |||
215 | /* Determine LCD IP Version */ | ||
216 | pm_runtime_get_sync(dev->dev); | ||
217 | switch (tilcdc_read(dev, LCDC_PID_REG)) { | ||
218 | case 0x4c100102: | ||
219 | priv->rev = 1; | ||
220 | break; | ||
221 | case 0x4f200800: | ||
222 | case 0x4f201000: | ||
223 | priv->rev = 2; | ||
224 | break; | ||
225 | default: | ||
226 | dev_warn(dev->dev, "Unknown PID Reg value 0x%08x, " | ||
227 | "defaulting to LCD revision 1\n", | ||
228 | tilcdc_read(dev, LCDC_PID_REG)); | ||
229 | priv->rev = 1; | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | pm_runtime_put_sync(dev->dev); | ||
234 | |||
235 | ret = modeset_init(dev); | ||
236 | if (ret < 0) { | ||
237 | dev_err(dev->dev, "failed to initialize mode setting\n"); | ||
238 | goto fail; | ||
239 | } | ||
240 | |||
241 | ret = drm_vblank_init(dev, 1); | ||
242 | if (ret < 0) { | ||
243 | dev_err(dev->dev, "failed to initialize vblank\n"); | ||
244 | goto fail; | ||
245 | } | ||
246 | |||
247 | pm_runtime_get_sync(dev->dev); | ||
248 | ret = drm_irq_install(dev); | ||
249 | pm_runtime_put_sync(dev->dev); | ||
250 | if (ret < 0) { | ||
251 | dev_err(dev->dev, "failed to install IRQ handler\n"); | ||
252 | goto fail; | ||
253 | } | ||
254 | |||
255 | platform_set_drvdata(pdev, dev); | ||
256 | |||
257 | priv->fbdev = drm_fbdev_cma_init(dev, 16, | ||
258 | dev->mode_config.num_crtc, | ||
259 | dev->mode_config.num_connector); | ||
260 | |||
261 | drm_kms_helper_poll_init(dev); | ||
262 | |||
263 | return 0; | ||
264 | |||
265 | fail: | ||
266 | tilcdc_unload(dev); | ||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static void tilcdc_preclose(struct drm_device *dev, struct drm_file *file) | ||
271 | { | ||
272 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
273 | |||
274 | tilcdc_crtc_cancel_page_flip(priv->crtc, file); | ||
275 | } | ||
276 | |||
277 | static void tilcdc_lastclose(struct drm_device *dev) | ||
278 | { | ||
279 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
280 | drm_fbdev_cma_restore_mode(priv->fbdev); | ||
281 | } | ||
282 | |||
283 | static irqreturn_t tilcdc_irq(DRM_IRQ_ARGS) | ||
284 | { | ||
285 | struct drm_device *dev = arg; | ||
286 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
287 | return tilcdc_crtc_irq(priv->crtc); | ||
288 | } | ||
289 | |||
290 | static void tilcdc_irq_preinstall(struct drm_device *dev) | ||
291 | { | ||
292 | tilcdc_clear_irqstatus(dev, 0xffffffff); | ||
293 | } | ||
294 | |||
295 | static int tilcdc_irq_postinstall(struct drm_device *dev) | ||
296 | { | ||
297 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
298 | |||
299 | /* enable FIFO underflow irq: */ | ||
300 | if (priv->rev == 1) { | ||
301 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); | ||
302 | } else { | ||
303 | tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA); | ||
304 | } | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static void tilcdc_irq_uninstall(struct drm_device *dev) | ||
310 | { | ||
311 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
312 | |||
313 | /* disable irqs that we might have enabled: */ | ||
314 | if (priv->rev == 1) { | ||
315 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, | ||
316 | LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); | ||
317 | tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); | ||
318 | } else { | ||
319 | tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, | ||
320 | LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | | ||
321 | LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA | | ||
322 | LCDC_FRAME_DONE); | ||
323 | } | ||
324 | |||
325 | } | ||
326 | |||
327 | static void enable_vblank(struct drm_device *dev, bool enable) | ||
328 | { | ||
329 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
330 | u32 reg, mask; | ||
331 | |||
332 | if (priv->rev == 1) { | ||
333 | reg = LCDC_DMA_CTRL_REG; | ||
334 | mask = LCDC_V1_END_OF_FRAME_INT_ENA; | ||
335 | } else { | ||
336 | reg = LCDC_INT_ENABLE_SET_REG; | ||
337 | mask = LCDC_V2_END_OF_FRAME0_INT_ENA | | ||
338 | LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; | ||
339 | } | ||
340 | |||
341 | if (enable) | ||
342 | tilcdc_set(dev, reg, mask); | ||
343 | else | ||
344 | tilcdc_clear(dev, reg, mask); | ||
345 | } | ||
346 | |||
347 | static int tilcdc_enable_vblank(struct drm_device *dev, int crtc) | ||
348 | { | ||
349 | enable_vblank(dev, true); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static void tilcdc_disable_vblank(struct drm_device *dev, int crtc) | ||
354 | { | ||
355 | enable_vblank(dev, false); | ||
356 | } | ||
357 | |||
358 | #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) | ||
359 | static const struct { | ||
360 | const char *name; | ||
361 | uint8_t rev; | ||
362 | uint8_t save; | ||
363 | uint32_t reg; | ||
364 | } registers[] = { | ||
365 | #define REG(rev, save, reg) { #reg, rev, save, reg } | ||
366 | /* exists in revision 1: */ | ||
367 | REG(1, false, LCDC_PID_REG), | ||
368 | REG(1, true, LCDC_CTRL_REG), | ||
369 | REG(1, false, LCDC_STAT_REG), | ||
370 | REG(1, true, LCDC_RASTER_CTRL_REG), | ||
371 | REG(1, true, LCDC_RASTER_TIMING_0_REG), | ||
372 | REG(1, true, LCDC_RASTER_TIMING_1_REG), | ||
373 | REG(1, true, LCDC_RASTER_TIMING_2_REG), | ||
374 | REG(1, true, LCDC_DMA_CTRL_REG), | ||
375 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_0_REG), | ||
376 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_0_REG), | ||
377 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_1_REG), | ||
378 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_1_REG), | ||
379 | /* new in revision 2: */ | ||
380 | REG(2, false, LCDC_RAW_STAT_REG), | ||
381 | REG(2, false, LCDC_MASKED_STAT_REG), | ||
382 | REG(2, false, LCDC_INT_ENABLE_SET_REG), | ||
383 | REG(2, false, LCDC_INT_ENABLE_CLR_REG), | ||
384 | REG(2, false, LCDC_END_OF_INT_IND_REG), | ||
385 | REG(2, true, LCDC_CLK_ENABLE_REG), | ||
386 | REG(2, true, LCDC_INT_ENABLE_SET_REG), | ||
387 | #undef REG | ||
388 | }; | ||
389 | #endif | ||
390 | |||
391 | #ifdef CONFIG_DEBUG_FS | ||
392 | static int tilcdc_regs_show(struct seq_file *m, void *arg) | ||
393 | { | ||
394 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
395 | struct drm_device *dev = node->minor->dev; | ||
396 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
397 | unsigned i; | ||
398 | |||
399 | pm_runtime_get_sync(dev->dev); | ||
400 | |||
401 | seq_printf(m, "revision: %d\n", priv->rev); | ||
402 | |||
403 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
404 | if (priv->rev >= registers[i].rev) | ||
405 | seq_printf(m, "%s:\t %08x\n", registers[i].name, | ||
406 | tilcdc_read(dev, registers[i].reg)); | ||
407 | |||
408 | pm_runtime_put_sync(dev->dev); | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int tilcdc_mm_show(struct seq_file *m, void *arg) | ||
414 | { | ||
415 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
416 | struct drm_device *dev = node->minor->dev; | ||
417 | return drm_mm_dump_table(m, dev->mm_private); | ||
418 | } | ||
419 | |||
420 | static struct drm_info_list tilcdc_debugfs_list[] = { | ||
421 | { "regs", tilcdc_regs_show, 0 }, | ||
422 | { "mm", tilcdc_mm_show, 0 }, | ||
423 | { "fb", drm_fb_cma_debugfs_show, 0 }, | ||
424 | }; | ||
425 | |||
426 | static int tilcdc_debugfs_init(struct drm_minor *minor) | ||
427 | { | ||
428 | struct drm_device *dev = minor->dev; | ||
429 | struct tilcdc_module *mod; | ||
430 | int ret; | ||
431 | |||
432 | ret = drm_debugfs_create_files(tilcdc_debugfs_list, | ||
433 | ARRAY_SIZE(tilcdc_debugfs_list), | ||
434 | minor->debugfs_root, minor); | ||
435 | |||
436 | list_for_each_entry(mod, &module_list, list) | ||
437 | if (mod->funcs->debugfs_init) | ||
438 | mod->funcs->debugfs_init(mod, minor); | ||
439 | |||
440 | if (ret) { | ||
441 | dev_err(dev->dev, "could not install tilcdc_debugfs_list\n"); | ||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | return ret; | ||
446 | } | ||
447 | |||
448 | static void tilcdc_debugfs_cleanup(struct drm_minor *minor) | ||
449 | { | ||
450 | struct tilcdc_module *mod; | ||
451 | drm_debugfs_remove_files(tilcdc_debugfs_list, | ||
452 | ARRAY_SIZE(tilcdc_debugfs_list), minor); | ||
453 | |||
454 | list_for_each_entry(mod, &module_list, list) | ||
455 | if (mod->funcs->debugfs_cleanup) | ||
456 | mod->funcs->debugfs_cleanup(mod, minor); | ||
457 | } | ||
458 | #endif | ||
459 | |||
460 | static const struct file_operations fops = { | ||
461 | .owner = THIS_MODULE, | ||
462 | .open = drm_open, | ||
463 | .release = drm_release, | ||
464 | .unlocked_ioctl = drm_ioctl, | ||
465 | #ifdef CONFIG_COMPAT | ||
466 | .compat_ioctl = drm_compat_ioctl, | ||
467 | #endif | ||
468 | .poll = drm_poll, | ||
469 | .read = drm_read, | ||
470 | .fasync = drm_fasync, | ||
471 | .llseek = no_llseek, | ||
472 | .mmap = drm_gem_cma_mmap, | ||
473 | }; | ||
474 | |||
475 | static struct drm_driver tilcdc_driver = { | ||
476 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, | ||
477 | .load = tilcdc_load, | ||
478 | .unload = tilcdc_unload, | ||
479 | .preclose = tilcdc_preclose, | ||
480 | .lastclose = tilcdc_lastclose, | ||
481 | .irq_handler = tilcdc_irq, | ||
482 | .irq_preinstall = tilcdc_irq_preinstall, | ||
483 | .irq_postinstall = tilcdc_irq_postinstall, | ||
484 | .irq_uninstall = tilcdc_irq_uninstall, | ||
485 | .get_vblank_counter = drm_vblank_count, | ||
486 | .enable_vblank = tilcdc_enable_vblank, | ||
487 | .disable_vblank = tilcdc_disable_vblank, | ||
488 | .gem_free_object = drm_gem_cma_free_object, | ||
489 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
490 | .dumb_create = drm_gem_cma_dumb_create, | ||
491 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | ||
492 | .dumb_destroy = drm_gem_cma_dumb_destroy, | ||
493 | #ifdef CONFIG_DEBUG_FS | ||
494 | .debugfs_init = tilcdc_debugfs_init, | ||
495 | .debugfs_cleanup = tilcdc_debugfs_cleanup, | ||
496 | #endif | ||
497 | .fops = &fops, | ||
498 | .name = "tilcdc", | ||
499 | .desc = "TI LCD Controller DRM", | ||
500 | .date = "20121205", | ||
501 | .major = 1, | ||
502 | .minor = 0, | ||
503 | }; | ||
504 | |||
505 | /* | ||
506 | * Power management: | ||
507 | */ | ||
508 | |||
509 | #ifdef CONFIG_PM_SLEEP | ||
510 | static int tilcdc_pm_suspend(struct device *dev) | ||
511 | { | ||
512 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
513 | struct tilcdc_drm_private *priv = ddev->dev_private; | ||
514 | unsigned i, n = 0; | ||
515 | |||
516 | drm_kms_helper_poll_disable(ddev); | ||
517 | |||
518 | /* Save register state: */ | ||
519 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
520 | if (registers[i].save && (priv->rev >= registers[i].rev)) | ||
521 | priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int tilcdc_pm_resume(struct device *dev) | ||
527 | { | ||
528 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
529 | struct tilcdc_drm_private *priv = ddev->dev_private; | ||
530 | unsigned i, n = 0; | ||
531 | |||
532 | /* Restore register state: */ | ||
533 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
534 | if (registers[i].save && (priv->rev >= registers[i].rev)) | ||
535 | tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]); | ||
536 | |||
537 | drm_kms_helper_poll_enable(ddev); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | #endif | ||
542 | |||
543 | static const struct dev_pm_ops tilcdc_pm_ops = { | ||
544 | SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume) | ||
545 | }; | ||
546 | |||
547 | /* | ||
548 | * Platform driver: | ||
549 | */ | ||
550 | |||
551 | static int tilcdc_pdev_probe(struct platform_device *pdev) | ||
552 | { | ||
553 | /* bail out early if no DT data: */ | ||
554 | if (!pdev->dev.of_node) { | ||
555 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
556 | return -ENXIO; | ||
557 | } | ||
558 | |||
559 | return drm_platform_init(&tilcdc_driver, pdev); | ||
560 | } | ||
561 | |||
562 | static int tilcdc_pdev_remove(struct platform_device *pdev) | ||
563 | { | ||
564 | drm_platform_exit(&tilcdc_driver, pdev); | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static struct of_device_id tilcdc_of_match[] = { | ||
570 | { .compatible = "ti,am33xx-tilcdc", }, | ||
571 | { }, | ||
572 | }; | ||
573 | MODULE_DEVICE_TABLE(of, tilcdc_of_match); | ||
574 | |||
575 | static struct platform_driver tilcdc_platform_driver = { | ||
576 | .probe = tilcdc_pdev_probe, | ||
577 | .remove = tilcdc_pdev_remove, | ||
578 | .driver = { | ||
579 | .owner = THIS_MODULE, | ||
580 | .name = "tilcdc", | ||
581 | .pm = &tilcdc_pm_ops, | ||
582 | .of_match_table = tilcdc_of_match, | ||
583 | }, | ||
584 | }; | ||
585 | |||
586 | static int __init tilcdc_drm_init(void) | ||
587 | { | ||
588 | DBG("init"); | ||
589 | tilcdc_tfp410_init(); | ||
590 | return platform_driver_register(&tilcdc_platform_driver); | ||
591 | } | ||
592 | |||
593 | static void __exit tilcdc_drm_fini(void) | ||
594 | { | ||
595 | DBG("fini"); | ||
596 | tilcdc_tfp410_fini(); | ||
597 | platform_driver_unregister(&tilcdc_platform_driver); | ||
598 | } | ||
599 | |||
600 | module_init(tilcdc_drm_init); | ||
601 | module_exit(tilcdc_drm_fini); | ||
602 | |||
603 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com"); | ||
604 | MODULE_DESCRIPTION("TI LCD Controller DRM Driver"); | ||
605 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h new file mode 100644 index 000000000000..8242b5a4307b --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef __TILCDC_DRV_H__ | ||
19 | #define __TILCDC_DRV_H__ | ||
20 | |||
21 | #include <linux/clk.h> | ||
22 | #include <linux/cpufreq.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/pm.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/of.h> | ||
29 | #include <linux/of_device.h> | ||
30 | #include <linux/list.h> | ||
31 | |||
32 | #include <drm/drmP.h> | ||
33 | #include <drm/drm_crtc_helper.h> | ||
34 | #include <drm/drm_gem_cma_helper.h> | ||
35 | #include <drm/drm_fb_cma_helper.h> | ||
36 | |||
37 | struct tilcdc_drm_private { | ||
38 | void __iomem *mmio; | ||
39 | |||
40 | struct clk *disp_clk; /* display dpll */ | ||
41 | struct clk *clk; /* functional clock */ | ||
42 | int rev; /* IP revision */ | ||
43 | |||
44 | /* don't attempt resolutions w/ higher W * H * Hz: */ | ||
45 | uint32_t max_bandwidth; | ||
46 | |||
47 | /* register contents saved across suspend/resume: */ | ||
48 | u32 saved_register[12]; | ||
49 | |||
50 | #ifdef CONFIG_CPU_FREQ | ||
51 | struct notifier_block freq_transition; | ||
52 | unsigned int lcd_fck_rate; | ||
53 | #endif | ||
54 | |||
55 | struct workqueue_struct *wq; | ||
56 | |||
57 | struct drm_fbdev_cma *fbdev; | ||
58 | |||
59 | struct drm_crtc *crtc; | ||
60 | |||
61 | unsigned int num_encoders; | ||
62 | struct drm_encoder *encoders[8]; | ||
63 | |||
64 | unsigned int num_connectors; | ||
65 | struct drm_connector *connectors[8]; | ||
66 | }; | ||
67 | |||
68 | /* Sub-module for display. Since we don't know at compile time what panels | ||
69 | * or display adapter(s) might be present (for ex, off chip dvi/tfp410, | ||
70 | * hdmi encoder, various lcd panels), the connector/encoder(s) are split into | ||
71 | * separate drivers. If they are probed and found to be present, they | ||
72 | * register themselves with tilcdc_register_module(). | ||
73 | */ | ||
74 | struct tilcdc_module; | ||
75 | |||
76 | struct tilcdc_module_ops { | ||
77 | /* create appropriate encoders/connectors: */ | ||
78 | int (*modeset_init)(struct tilcdc_module *mod, struct drm_device *dev); | ||
79 | void (*destroy)(struct tilcdc_module *mod); | ||
80 | #ifdef CONFIG_DEBUG_FS | ||
81 | /* create debugfs nodes (can be NULL): */ | ||
82 | int (*debugfs_init)(struct tilcdc_module *mod, struct drm_minor *minor); | ||
83 | /* cleanup debugfs nodes (can be NULL): */ | ||
84 | void (*debugfs_cleanup)(struct tilcdc_module *mod, struct drm_minor *minor); | ||
85 | #endif | ||
86 | }; | ||
87 | |||
88 | struct tilcdc_module { | ||
89 | const char *name; | ||
90 | struct list_head list; | ||
91 | const struct tilcdc_module_ops *funcs; | ||
92 | }; | ||
93 | |||
94 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, | ||
95 | const struct tilcdc_module_ops *funcs); | ||
96 | void tilcdc_module_cleanup(struct tilcdc_module *mod); | ||
97 | |||
98 | |||
99 | /* Panel config that needs to be set in the crtc, but is not coming from | ||
100 | * the mode timings. The display module is expected to call | ||
101 | * tilcdc_crtc_set_panel_info() to set this during modeset. | ||
102 | */ | ||
103 | struct tilcdc_panel_info { | ||
104 | |||
105 | /* AC Bias Pin Frequency */ | ||
106 | uint32_t ac_bias; | ||
107 | |||
108 | /* AC Bias Pin Transitions per Interrupt */ | ||
109 | uint32_t ac_bias_intrpt; | ||
110 | |||
111 | /* DMA burst size */ | ||
112 | uint32_t dma_burst_sz; | ||
113 | |||
114 | /* Bits per pixel */ | ||
115 | uint32_t bpp; | ||
116 | |||
117 | /* FIFO DMA Request Delay */ | ||
118 | uint32_t fdd; | ||
119 | |||
120 | /* TFT Alternative Signal Mapping (Only for active) */ | ||
121 | bool tft_alt_mode; | ||
122 | |||
123 | /* Invert pixel clock */ | ||
124 | bool invert_pxl_clk; | ||
125 | |||
126 | /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ | ||
127 | uint32_t sync_edge; | ||
128 | |||
129 | /* Horizontal and Vertical Sync: Control: 0=ignore */ | ||
130 | uint32_t sync_ctrl; | ||
131 | |||
132 | /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ | ||
133 | uint32_t raster_order; | ||
134 | |||
135 | /* DMA FIFO threshold */ | ||
136 | uint32_t fifo_th; | ||
137 | }; | ||
138 | |||
139 | #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) | ||
140 | |||
141 | struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev); | ||
142 | void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); | ||
143 | irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc); | ||
144 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc); | ||
145 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | ||
146 | const struct tilcdc_panel_info *info); | ||
147 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); | ||
148 | int tilcdc_crtc_max_width(struct drm_crtc *crtc); | ||
149 | |||
150 | #endif /* __TILCDC_DRV_H__ */ | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h new file mode 100644 index 000000000000..17fd1b45428a --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef __TILCDC_REGS_H__ | ||
19 | #define __TILCDC_REGS_H__ | ||
20 | |||
21 | /* LCDC register definitions, based on da8xx-fb */ | ||
22 | |||
23 | #include <linux/bitops.h> | ||
24 | |||
25 | #include "tilcdc_drv.h" | ||
26 | |||
27 | /* LCDC Status Register */ | ||
28 | #define LCDC_END_OF_FRAME1 BIT(9) | ||
29 | #define LCDC_END_OF_FRAME0 BIT(8) | ||
30 | #define LCDC_PL_LOAD_DONE BIT(6) | ||
31 | #define LCDC_FIFO_UNDERFLOW BIT(5) | ||
32 | #define LCDC_SYNC_LOST BIT(2) | ||
33 | #define LCDC_FRAME_DONE BIT(0) | ||
34 | |||
35 | /* LCDC DMA Control Register */ | ||
36 | #define LCDC_DMA_BURST_SIZE(x) ((x) << 4) | ||
37 | #define LCDC_DMA_BURST_1 0x0 | ||
38 | #define LCDC_DMA_BURST_2 0x1 | ||
39 | #define LCDC_DMA_BURST_4 0x2 | ||
40 | #define LCDC_DMA_BURST_8 0x3 | ||
41 | #define LCDC_DMA_BURST_16 0x4 | ||
42 | #define LCDC_V1_END_OF_FRAME_INT_ENA BIT(2) | ||
43 | #define LCDC_V2_END_OF_FRAME0_INT_ENA BIT(8) | ||
44 | #define LCDC_V2_END_OF_FRAME1_INT_ENA BIT(9) | ||
45 | #define LCDC_DUAL_FRAME_BUFFER_ENABLE BIT(0) | ||
46 | |||
47 | /* LCDC Control Register */ | ||
48 | #define LCDC_CLK_DIVISOR(x) ((x) << 8) | ||
49 | #define LCDC_RASTER_MODE 0x01 | ||
50 | |||
51 | /* LCDC Raster Control Register */ | ||
52 | #define LCDC_PALETTE_LOAD_MODE(x) ((x) << 20) | ||
53 | #define PALETTE_AND_DATA 0x00 | ||
54 | #define PALETTE_ONLY 0x01 | ||
55 | #define DATA_ONLY 0x02 | ||
56 | |||
57 | #define LCDC_MONO_8BIT_MODE BIT(9) | ||
58 | #define LCDC_RASTER_ORDER BIT(8) | ||
59 | #define LCDC_TFT_MODE BIT(7) | ||
60 | #define LCDC_V1_UNDERFLOW_INT_ENA BIT(6) | ||
61 | #define LCDC_V2_UNDERFLOW_INT_ENA BIT(5) | ||
62 | #define LCDC_V1_PL_INT_ENA BIT(4) | ||
63 | #define LCDC_V2_PL_INT_ENA BIT(6) | ||
64 | #define LCDC_MONOCHROME_MODE BIT(1) | ||
65 | #define LCDC_RASTER_ENABLE BIT(0) | ||
66 | #define LCDC_TFT_ALT_ENABLE BIT(23) | ||
67 | #define LCDC_STN_565_ENABLE BIT(24) | ||
68 | #define LCDC_V2_DMA_CLK_EN BIT(2) | ||
69 | #define LCDC_V2_LIDD_CLK_EN BIT(1) | ||
70 | #define LCDC_V2_CORE_CLK_EN BIT(0) | ||
71 | #define LCDC_V2_LPP_B10 26 | ||
72 | #define LCDC_V2_TFT_24BPP_MODE BIT(25) | ||
73 | #define LCDC_V2_TFT_24BPP_UNPACK BIT(26) | ||
74 | |||
75 | /* LCDC Raster Timing 2 Register */ | ||
76 | #define LCDC_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) | ||
77 | #define LCDC_AC_BIAS_FREQUENCY(x) ((x) << 8) | ||
78 | #define LCDC_SYNC_CTRL BIT(25) | ||
79 | #define LCDC_SYNC_EDGE BIT(24) | ||
80 | #define LCDC_INVERT_PIXEL_CLOCK BIT(22) | ||
81 | #define LCDC_INVERT_HSYNC BIT(21) | ||
82 | #define LCDC_INVERT_VSYNC BIT(20) | ||
83 | |||
84 | /* LCDC Block */ | ||
85 | #define LCDC_PID_REG 0x0 | ||
86 | #define LCDC_CTRL_REG 0x4 | ||
87 | #define LCDC_STAT_REG 0x8 | ||
88 | #define LCDC_RASTER_CTRL_REG 0x28 | ||
89 | #define LCDC_RASTER_TIMING_0_REG 0x2c | ||
90 | #define LCDC_RASTER_TIMING_1_REG 0x30 | ||
91 | #define LCDC_RASTER_TIMING_2_REG 0x34 | ||
92 | #define LCDC_DMA_CTRL_REG 0x40 | ||
93 | #define LCDC_DMA_FB_BASE_ADDR_0_REG 0x44 | ||
94 | #define LCDC_DMA_FB_CEILING_ADDR_0_REG 0x48 | ||
95 | #define LCDC_DMA_FB_BASE_ADDR_1_REG 0x4c | ||
96 | #define LCDC_DMA_FB_CEILING_ADDR_1_REG 0x50 | ||
97 | |||
98 | /* Interrupt Registers available only in Version 2 */ | ||
99 | #define LCDC_RAW_STAT_REG 0x58 | ||
100 | #define LCDC_MASKED_STAT_REG 0x5c | ||
101 | #define LCDC_INT_ENABLE_SET_REG 0x60 | ||
102 | #define LCDC_INT_ENABLE_CLR_REG 0x64 | ||
103 | #define LCDC_END_OF_INT_IND_REG 0x68 | ||
104 | |||
105 | /* Clock registers available only on Version 2 */ | ||
106 | #define LCDC_CLK_ENABLE_REG 0x6c | ||
107 | #define LCDC_CLK_RESET_REG 0x70 | ||
108 | #define LCDC_CLK_MAIN_RESET BIT(3) | ||
109 | |||
110 | |||
111 | /* | ||
112 | * Helpers: | ||
113 | */ | ||
114 | |||
115 | static inline void tilcdc_write(struct drm_device *dev, u32 reg, u32 data) | ||
116 | { | ||
117 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
118 | iowrite32(data, priv->mmio + reg); | ||
119 | } | ||
120 | |||
121 | static inline u32 tilcdc_read(struct drm_device *dev, u32 reg) | ||
122 | { | ||
123 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
124 | return ioread32(priv->mmio + reg); | ||
125 | } | ||
126 | |||
127 | static inline void tilcdc_set(struct drm_device *dev, u32 reg, u32 mask) | ||
128 | { | ||
129 | tilcdc_write(dev, reg, tilcdc_read(dev, reg) | mask); | ||
130 | } | ||
131 | |||
132 | static inline void tilcdc_clear(struct drm_device *dev, u32 reg, u32 mask) | ||
133 | { | ||
134 | tilcdc_write(dev, reg, tilcdc_read(dev, reg) & ~mask); | ||
135 | } | ||
136 | |||
137 | /* the register to read/clear irqstatus differs between v1 and v2 of the IP */ | ||
138 | static inline u32 tilcdc_irqstatus_reg(struct drm_device *dev) | ||
139 | { | ||
140 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
141 | return (priv->rev == 2) ? LCDC_MASKED_STAT_REG : LCDC_STAT_REG; | ||
142 | } | ||
143 | |||
144 | static inline u32 tilcdc_read_irqstatus(struct drm_device *dev) | ||
145 | { | ||
146 | return tilcdc_read(dev, tilcdc_irqstatus_reg(dev)); | ||
147 | } | ||
148 | |||
149 | static inline void tilcdc_clear_irqstatus(struct drm_device *dev, u32 mask) | ||
150 | { | ||
151 | tilcdc_write(dev, tilcdc_irqstatus_reg(dev), mask); | ||
152 | } | ||
153 | |||
154 | #endif /* __TILCDC_REGS_H__ */ | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c new file mode 100644 index 000000000000..58d487ba2414 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/of_i2c.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include <linux/of_gpio.h> | ||
22 | #include <linux/pinctrl/pinmux.h> | ||
23 | #include <linux/pinctrl/consumer.h> | ||
24 | |||
25 | #include "tilcdc_drv.h" | ||
26 | |||
27 | struct tfp410_module { | ||
28 | struct tilcdc_module base; | ||
29 | struct i2c_adapter *i2c; | ||
30 | int gpio; | ||
31 | }; | ||
32 | #define to_tfp410_module(x) container_of(x, struct tfp410_module, base) | ||
33 | |||
34 | |||
35 | static const struct tilcdc_panel_info dvi_info = { | ||
36 | .ac_bias = 255, | ||
37 | .ac_bias_intrpt = 0, | ||
38 | .dma_burst_sz = 16, | ||
39 | .bpp = 16, | ||
40 | .fdd = 0x80, | ||
41 | .tft_alt_mode = 0, | ||
42 | .sync_edge = 0, | ||
43 | .sync_ctrl = 1, | ||
44 | .raster_order = 0, | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * Encoder: | ||
49 | */ | ||
50 | |||
51 | struct tfp410_encoder { | ||
52 | struct drm_encoder base; | ||
53 | struct tfp410_module *mod; | ||
54 | int dpms; | ||
55 | }; | ||
56 | #define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base) | ||
57 | |||
58 | |||
59 | static void tfp410_encoder_destroy(struct drm_encoder *encoder) | ||
60 | { | ||
61 | struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); | ||
62 | drm_encoder_cleanup(encoder); | ||
63 | kfree(tfp410_encoder); | ||
64 | } | ||
65 | |||
66 | static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
67 | { | ||
68 | struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); | ||
69 | |||
70 | if (tfp410_encoder->dpms == mode) | ||
71 | return; | ||
72 | |||
73 | if (mode == DRM_MODE_DPMS_ON) { | ||
74 | DBG("Power on"); | ||
75 | gpio_direction_output(tfp410_encoder->mod->gpio, 1); | ||
76 | } else { | ||
77 | DBG("Power off"); | ||
78 | gpio_direction_output(tfp410_encoder->mod->gpio, 0); | ||
79 | } | ||
80 | |||
81 | tfp410_encoder->dpms = mode; | ||
82 | } | ||
83 | |||
84 | static bool tfp410_encoder_mode_fixup(struct drm_encoder *encoder, | ||
85 | const struct drm_display_mode *mode, | ||
86 | struct drm_display_mode *adjusted_mode) | ||
87 | { | ||
88 | /* nothing needed */ | ||
89 | return true; | ||
90 | } | ||
91 | |||
92 | static void tfp410_encoder_prepare(struct drm_encoder *encoder) | ||
93 | { | ||
94 | tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | ||
95 | tilcdc_crtc_set_panel_info(encoder->crtc, &dvi_info); | ||
96 | } | ||
97 | |||
98 | static void tfp410_encoder_commit(struct drm_encoder *encoder) | ||
99 | { | ||
100 | tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_ON); | ||
101 | } | ||
102 | |||
103 | static void tfp410_encoder_mode_set(struct drm_encoder *encoder, | ||
104 | struct drm_display_mode *mode, | ||
105 | struct drm_display_mode *adjusted_mode) | ||
106 | { | ||
107 | /* nothing needed */ | ||
108 | } | ||
109 | |||
110 | static const struct drm_encoder_funcs tfp410_encoder_funcs = { | ||
111 | .destroy = tfp410_encoder_destroy, | ||
112 | }; | ||
113 | |||
114 | static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = { | ||
115 | .dpms = tfp410_encoder_dpms, | ||
116 | .mode_fixup = tfp410_encoder_mode_fixup, | ||
117 | .prepare = tfp410_encoder_prepare, | ||
118 | .commit = tfp410_encoder_commit, | ||
119 | .mode_set = tfp410_encoder_mode_set, | ||
120 | }; | ||
121 | |||
122 | static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, | ||
123 | struct tfp410_module *mod) | ||
124 | { | ||
125 | struct tfp410_encoder *tfp410_encoder; | ||
126 | struct drm_encoder *encoder; | ||
127 | int ret; | ||
128 | |||
129 | tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL); | ||
130 | if (!tfp410_encoder) { | ||
131 | dev_err(dev->dev, "allocation failed\n"); | ||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | tfp410_encoder->dpms = DRM_MODE_DPMS_OFF; | ||
136 | tfp410_encoder->mod = mod; | ||
137 | |||
138 | encoder = &tfp410_encoder->base; | ||
139 | encoder->possible_crtcs = 1; | ||
140 | |||
141 | ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs, | ||
142 | DRM_MODE_ENCODER_TMDS); | ||
143 | if (ret < 0) | ||
144 | goto fail; | ||
145 | |||
146 | drm_encoder_helper_add(encoder, &tfp410_encoder_helper_funcs); | ||
147 | |||
148 | return encoder; | ||
149 | |||
150 | fail: | ||
151 | tfp410_encoder_destroy(encoder); | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Connector: | ||
157 | */ | ||
158 | |||
159 | struct tfp410_connector { | ||
160 | struct drm_connector base; | ||
161 | |||
162 | struct drm_encoder *encoder; /* our connected encoder */ | ||
163 | struct tfp410_module *mod; | ||
164 | }; | ||
165 | #define to_tfp410_connector(x) container_of(x, struct tfp410_connector, base) | ||
166 | |||
167 | |||
168 | static void tfp410_connector_destroy(struct drm_connector *connector) | ||
169 | { | ||
170 | struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); | ||
171 | drm_connector_cleanup(connector); | ||
172 | kfree(tfp410_connector); | ||
173 | } | ||
174 | |||
175 | static enum drm_connector_status tfp410_connector_detect( | ||
176 | struct drm_connector *connector, | ||
177 | bool force) | ||
178 | { | ||
179 | struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); | ||
180 | |||
181 | if (drm_probe_ddc(tfp410_connector->mod->i2c)) | ||
182 | return connector_status_connected; | ||
183 | |||
184 | return connector_status_unknown; | ||
185 | } | ||
186 | |||
187 | static int tfp410_connector_get_modes(struct drm_connector *connector) | ||
188 | { | ||
189 | struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); | ||
190 | struct edid *edid; | ||
191 | int ret = 0; | ||
192 | |||
193 | edid = drm_get_edid(connector, tfp410_connector->mod->i2c); | ||
194 | |||
195 | drm_mode_connector_update_edid_property(connector, edid); | ||
196 | |||
197 | if (edid) { | ||
198 | ret = drm_add_edid_modes(connector, edid); | ||
199 | kfree(edid); | ||
200 | } | ||
201 | |||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | static int tfp410_connector_mode_valid(struct drm_connector *connector, | ||
206 | struct drm_display_mode *mode) | ||
207 | { | ||
208 | struct tilcdc_drm_private *priv = connector->dev->dev_private; | ||
209 | /* our only constraints are what the crtc can generate: */ | ||
210 | return tilcdc_crtc_mode_valid(priv->crtc, mode); | ||
211 | } | ||
212 | |||
213 | static struct drm_encoder *tfp410_connector_best_encoder( | ||
214 | struct drm_connector *connector) | ||
215 | { | ||
216 | struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); | ||
217 | return tfp410_connector->encoder; | ||
218 | } | ||
219 | |||
220 | static const struct drm_connector_funcs tfp410_connector_funcs = { | ||
221 | .destroy = tfp410_connector_destroy, | ||
222 | .dpms = drm_helper_connector_dpms, | ||
223 | .detect = tfp410_connector_detect, | ||
224 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
225 | }; | ||
226 | |||
227 | static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = { | ||
228 | .get_modes = tfp410_connector_get_modes, | ||
229 | .mode_valid = tfp410_connector_mode_valid, | ||
230 | .best_encoder = tfp410_connector_best_encoder, | ||
231 | }; | ||
232 | |||
233 | static struct drm_connector *tfp410_connector_create(struct drm_device *dev, | ||
234 | struct tfp410_module *mod, struct drm_encoder *encoder) | ||
235 | { | ||
236 | struct tfp410_connector *tfp410_connector; | ||
237 | struct drm_connector *connector; | ||
238 | int ret; | ||
239 | |||
240 | tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL); | ||
241 | if (!tfp410_connector) { | ||
242 | dev_err(dev->dev, "allocation failed\n"); | ||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | tfp410_connector->encoder = encoder; | ||
247 | tfp410_connector->mod = mod; | ||
248 | |||
249 | connector = &tfp410_connector->base; | ||
250 | |||
251 | drm_connector_init(dev, connector, &tfp410_connector_funcs, | ||
252 | DRM_MODE_CONNECTOR_DVID); | ||
253 | drm_connector_helper_add(connector, &tfp410_connector_helper_funcs); | ||
254 | |||
255 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | | ||
256 | DRM_CONNECTOR_POLL_DISCONNECT; | ||
257 | |||
258 | connector->interlace_allowed = 0; | ||
259 | connector->doublescan_allowed = 0; | ||
260 | |||
261 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
262 | if (ret) | ||
263 | goto fail; | ||
264 | |||
265 | drm_sysfs_connector_add(connector); | ||
266 | |||
267 | return connector; | ||
268 | |||
269 | fail: | ||
270 | tfp410_connector_destroy(connector); | ||
271 | return NULL; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Module: | ||
276 | */ | ||
277 | |||
278 | static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) | ||
279 | { | ||
280 | struct tfp410_module *tfp410_mod = to_tfp410_module(mod); | ||
281 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
282 | struct drm_encoder *encoder; | ||
283 | struct drm_connector *connector; | ||
284 | |||
285 | encoder = tfp410_encoder_create(dev, tfp410_mod); | ||
286 | if (!encoder) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | connector = tfp410_connector_create(dev, tfp410_mod, encoder); | ||
290 | if (!connector) | ||
291 | return -ENOMEM; | ||
292 | |||
293 | priv->encoders[priv->num_encoders++] = encoder; | ||
294 | priv->connectors[priv->num_connectors++] = connector; | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static void tfp410_destroy(struct tilcdc_module *mod) | ||
300 | { | ||
301 | struct tfp410_module *tfp410_mod = to_tfp410_module(mod); | ||
302 | |||
303 | if (tfp410_mod->i2c) | ||
304 | i2c_put_adapter(tfp410_mod->i2c); | ||
305 | |||
306 | if (!IS_ERR_VALUE(tfp410_mod->gpio)) | ||
307 | gpio_free(tfp410_mod->gpio); | ||
308 | |||
309 | tilcdc_module_cleanup(mod); | ||
310 | kfree(tfp410_mod); | ||
311 | } | ||
312 | |||
313 | static const struct tilcdc_module_ops tfp410_module_ops = { | ||
314 | .modeset_init = tfp410_modeset_init, | ||
315 | .destroy = tfp410_destroy, | ||
316 | }; | ||
317 | |||
318 | /* | ||
319 | * Device: | ||
320 | */ | ||
321 | |||
322 | static struct of_device_id tfp410_of_match[]; | ||
323 | |||
324 | static int tfp410_probe(struct platform_device *pdev) | ||
325 | { | ||
326 | struct device_node *node = pdev->dev.of_node; | ||
327 | struct device_node *i2c_node; | ||
328 | struct tfp410_module *tfp410_mod; | ||
329 | struct tilcdc_module *mod; | ||
330 | struct pinctrl *pinctrl; | ||
331 | uint32_t i2c_phandle; | ||
332 | int ret = -EINVAL; | ||
333 | |||
334 | /* bail out early if no DT data: */ | ||
335 | if (!node) { | ||
336 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
337 | return -ENXIO; | ||
338 | } | ||
339 | |||
340 | tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL); | ||
341 | if (!tfp410_mod) | ||
342 | return -ENOMEM; | ||
343 | |||
344 | mod = &tfp410_mod->base; | ||
345 | |||
346 | tilcdc_module_init(mod, "tfp410", &tfp410_module_ops); | ||
347 | |||
348 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
349 | if (IS_ERR(pinctrl)) | ||
350 | dev_warn(&pdev->dev, "pins are not configured\n"); | ||
351 | |||
352 | if (of_property_read_u32(node, "i2c", &i2c_phandle)) { | ||
353 | dev_err(&pdev->dev, "could not get i2c bus phandle\n"); | ||
354 | goto fail; | ||
355 | } | ||
356 | |||
357 | i2c_node = of_find_node_by_phandle(i2c_phandle); | ||
358 | if (!i2c_node) { | ||
359 | dev_err(&pdev->dev, "could not get i2c bus node\n"); | ||
360 | goto fail; | ||
361 | } | ||
362 | |||
363 | tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node); | ||
364 | if (!tfp410_mod->i2c) { | ||
365 | dev_err(&pdev->dev, "could not get i2c\n"); | ||
366 | goto fail; | ||
367 | } | ||
368 | |||
369 | of_node_put(i2c_node); | ||
370 | |||
371 | tfp410_mod->gpio = of_get_named_gpio_flags(node, "powerdn-gpio", | ||
372 | 0, NULL); | ||
373 | if (IS_ERR_VALUE(tfp410_mod->gpio)) { | ||
374 | dev_warn(&pdev->dev, "No power down GPIO\n"); | ||
375 | } else { | ||
376 | ret = gpio_request(tfp410_mod->gpio, "DVI_PDn"); | ||
377 | if (ret) { | ||
378 | dev_err(&pdev->dev, "could not get DVI_PDn gpio\n"); | ||
379 | goto fail; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | return 0; | ||
384 | |||
385 | fail: | ||
386 | tfp410_destroy(mod); | ||
387 | return ret; | ||
388 | } | ||
389 | |||
390 | static int tfp410_remove(struct platform_device *pdev) | ||
391 | { | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static struct of_device_id tfp410_of_match[] = { | ||
396 | { .compatible = "ti,tilcdc,tfp410", }, | ||
397 | { }, | ||
398 | }; | ||
399 | MODULE_DEVICE_TABLE(of, tfp410_of_match); | ||
400 | |||
401 | struct platform_driver tfp410_driver = { | ||
402 | .probe = tfp410_probe, | ||
403 | .remove = tfp410_remove, | ||
404 | .driver = { | ||
405 | .owner = THIS_MODULE, | ||
406 | .name = "tfp410", | ||
407 | .of_match_table = tfp410_of_match, | ||
408 | }, | ||
409 | }; | ||
410 | |||
411 | int __init tilcdc_tfp410_init(void) | ||
412 | { | ||
413 | return platform_driver_register(&tfp410_driver); | ||
414 | } | ||
415 | |||
416 | void __exit tilcdc_tfp410_fini(void) | ||
417 | { | ||
418 | platform_driver_unregister(&tfp410_driver); | ||
419 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h new file mode 100644 index 000000000000..5b800f1f6aa5 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments | ||
3 | * Author: Rob Clark <robdclark@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef __TILCDC_TFP410_H__ | ||
19 | #define __TILCDC_TFP410_H__ | ||
20 | |||
21 | /* sub-module for tfp410 dvi adaptor */ | ||
22 | |||
23 | int tilcdc_tfp410_init(void); | ||
24 | void tilcdc_tfp410_fini(void); | ||
25 | |||
26 | #endif /* __TILCDC_TFP410_H__ */ | ||