diff options
author | Dave Airlie <airlied@redhat.com> | 2013-02-20 18:31:47 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-02-20 18:31:47 -0500 |
commit | ca18e1426bb2db987b67030256477c9571aebd09 (patch) | |
tree | 72e7cbe5f00f90390d00f5d3d4c9bdec79e48bbe | |
parent | 74e1697478ffdee0e12e48db024a9b3677fd8cee (diff) | |
parent | 0d4bbaf9f3e5b9f52150ddc5a4ee8b0ab83a440b (diff) |
Merge branch 'tilcdc-next' of git://people.freedesktop.org/~robclark/linux into drm-next
KMS driver for TI LCD controller
* 'tilcdc-next' of git://people.freedesktop.org/~robclark/linux:
drm/tilcdc: add support for LCD panels (v5)
drm/tilcdc: add encoder slave (v2)
drm/i2c: nxp-tda998x (v3)
drm/tilcdc: add TI LCD Controller DRM driver (v4)
drm/nouveau: use i2c encoder helper wrappers
drm: i2c encoder helper wrappers
drm/cma: add debugfs helpers
drm: small fix in drm_send_vblank_event()
drm: Don't set the plane->fb to NULL on successfull set_plane
drm/cma-helper: fixup compilation
Conflicts:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_fb_cma_helper.c
29 files changed, 4066 insertions, 26 deletions
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/panel.txt b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt new file mode 100644 index 000000000000..9301c330d1a6 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt | |||
@@ -0,0 +1,59 @@ | |||
1 | Device-Tree bindings for tilcdc DRM generic panel output driver | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: value should be "ti,tilcdc,panel". | ||
5 | - panel-info: configuration info to configure LCDC correctly for the panel | ||
6 | - ac-bias: AC Bias Pin Frequency | ||
7 | - ac-bias-intrpt: AC Bias Pin Transitions per Interrupt | ||
8 | - dma-burst-sz: DMA burst size | ||
9 | - bpp: Bits per pixel | ||
10 | - fdd: FIFO DMA Request Delay | ||
11 | - sync-edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling | ||
12 | - sync-ctrl: Horizontal and Vertical Sync: Control: 0=ignore | ||
13 | - raster-order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most | ||
14 | - fifo-th: DMA FIFO threshold | ||
15 | - display-timings: typical videomode of lcd panel. Multiple video modes | ||
16 | can be listed if the panel supports multiple timings, but the 'native-mode' | ||
17 | should be the preferred/default resolution. Refer to | ||
18 | Documentation/devicetree/bindings/video/display-timing.txt for display | ||
19 | timing binding details. | ||
20 | |||
21 | Recommended properties: | ||
22 | - pinctrl-names, pinctrl-0: the pincontrol settings to configure | ||
23 | muxing properly for pins that connect to TFP410 device | ||
24 | |||
25 | Example: | ||
26 | |||
27 | /* Settings for CDTech_S035Q01 / LCD3 cape: */ | ||
28 | lcd3 { | ||
29 | compatible = "ti,tilcdc,panel"; | ||
30 | pinctrl-names = "default"; | ||
31 | pinctrl-0 = <&bone_lcd3_cape_lcd_pins>; | ||
32 | panel-info { | ||
33 | ac-bias = <255>; | ||
34 | ac-bias-intrpt = <0>; | ||
35 | dma-burst-sz = <16>; | ||
36 | bpp = <16>; | ||
37 | fdd = <0x80>; | ||
38 | sync-edge = <0>; | ||
39 | sync-ctrl = <1>; | ||
40 | raster-order = <0>; | ||
41 | fifo-th = <0>; | ||
42 | }; | ||
43 | display-timings { | ||
44 | native-mode = <&timing0>; | ||
45 | timing0: 320x240 { | ||
46 | hactive = <320>; | ||
47 | vactive = <240>; | ||
48 | hback-porch = <21>; | ||
49 | hfront-porch = <58>; | ||
50 | hsync-len = <47>; | ||
51 | vback-porch = <11>; | ||
52 | vfront-porch = <23>; | ||
53 | vsync-len = <2>; | ||
54 | clock-frequency = <8000000>; | ||
55 | hsync-active = <0>; | ||
56 | vsync-active = <0>; | ||
57 | }; | ||
58 | }; | ||
59 | }; | ||
diff --git a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt new file mode 100644 index 000000000000..3d2c52460dca --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt | |||
@@ -0,0 +1,18 @@ | |||
1 | Device-Tree bindings for tilcdc DRM encoder slave output driver | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: value should be "ti,tilcdc,slave". | ||
5 | - i2c: the phandle for the i2c device the encoder slave is connected to | ||
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 | |||
11 | Example: | ||
12 | |||
13 | hdmi { | ||
14 | compatible = "ti,tilcdc,slave"; | ||
15 | i2c = <&i2c0>; | ||
16 | pinctrl-names = "default"; | ||
17 | pinctrl-0 = <&nxp_hdmi_bonelt_pins>; | ||
18 | }; | ||
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 0ce5f52ac56e..f8dae851130c 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
@@ -217,3 +217,5 @@ source "drivers/gpu/drm/shmobile/Kconfig" | |||
217 | source "drivers/gpu/drm/tegra/Kconfig" | 217 | source "drivers/gpu/drm/tegra/Kconfig" |
218 | 218 | ||
219 | source "drivers/gpu/drm/omapdrm/Kconfig" | 219 | source "drivers/gpu/drm/omapdrm/Kconfig" |
220 | |||
221 | source "drivers/gpu/drm/tilcdc/Kconfig" | ||
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index b6b43cbc18e4..0d59b24f8d23 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -51,4 +51,5 @@ 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_OMAP) += omapdrm/ | 53 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ |
54 | obj-$(CONFIG_DRM_TILCDC) += tilcdc/ | ||
54 | obj-y += i2c/ | 55 | obj-y += i2c/ |
diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c index 63e733408b6d..48c52f7df4e6 100644 --- a/drivers/gpu/drm/drm_encoder_slave.c +++ b/drivers/gpu/drm/drm_encoder_slave.c | |||
@@ -123,3 +123,66 @@ void drm_i2c_encoder_destroy(struct drm_encoder *drm_encoder) | |||
123 | module_put(module); | 123 | module_put(module); |
124 | } | 124 | } |
125 | EXPORT_SYMBOL(drm_i2c_encoder_destroy); | 125 | EXPORT_SYMBOL(drm_i2c_encoder_destroy); |
126 | |||
127 | /* | ||
128 | * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: | ||
129 | */ | ||
130 | |||
131 | static inline struct drm_encoder_slave_funcs * | ||
132 | get_slave_funcs(struct drm_encoder *enc) | ||
133 | { | ||
134 | return to_encoder_slave(enc)->slave_funcs; | ||
135 | } | ||
136 | |||
137 | void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
138 | { | ||
139 | get_slave_funcs(encoder)->dpms(encoder, mode); | ||
140 | } | ||
141 | EXPORT_SYMBOL(drm_i2c_encoder_dpms); | ||
142 | |||
143 | bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, | ||
144 | const struct drm_display_mode *mode, | ||
145 | struct drm_display_mode *adjusted_mode) | ||
146 | { | ||
147 | return get_slave_funcs(encoder)->mode_fixup(encoder, mode, adjusted_mode); | ||
148 | } | ||
149 | EXPORT_SYMBOL(drm_i2c_encoder_mode_fixup); | ||
150 | |||
151 | void drm_i2c_encoder_prepare(struct drm_encoder *encoder) | ||
152 | { | ||
153 | drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | ||
154 | } | ||
155 | EXPORT_SYMBOL(drm_i2c_encoder_prepare); | ||
156 | |||
157 | void drm_i2c_encoder_commit(struct drm_encoder *encoder) | ||
158 | { | ||
159 | drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_ON); | ||
160 | } | ||
161 | EXPORT_SYMBOL(drm_i2c_encoder_commit); | ||
162 | |||
163 | void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, | ||
164 | struct drm_display_mode *mode, | ||
165 | struct drm_display_mode *adjusted_mode) | ||
166 | { | ||
167 | get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); | ||
168 | } | ||
169 | EXPORT_SYMBOL(drm_i2c_encoder_mode_set); | ||
170 | |||
171 | enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, | ||
172 | struct drm_connector *connector) | ||
173 | { | ||
174 | return get_slave_funcs(encoder)->detect(encoder, connector); | ||
175 | } | ||
176 | EXPORT_SYMBOL(drm_i2c_encoder_detect); | ||
177 | |||
178 | void drm_i2c_encoder_save(struct drm_encoder *encoder) | ||
179 | { | ||
180 | get_slave_funcs(encoder)->save(encoder); | ||
181 | } | ||
182 | EXPORT_SYMBOL(drm_i2c_encoder_save); | ||
183 | |||
184 | void drm_i2c_encoder_restore(struct drm_encoder *encoder) | ||
185 | { | ||
186 | get_slave_funcs(encoder)->restore(encoder); | ||
187 | } | ||
188 | EXPORT_SYMBOL(drm_i2c_encoder_restore); | ||
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 1c8549dae99a..0b5af7d0edb1 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c | |||
@@ -180,6 +180,59 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, | |||
180 | } | 180 | } |
181 | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); | 181 | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); |
182 | 182 | ||
183 | #ifdef CONFIG_DEBUG_FS | ||
184 | /** | ||
185 | * drm_fb_cma_describe() - Helper to dump information about a single | ||
186 | * CMA framebuffer object | ||
187 | */ | ||
188 | void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) | ||
189 | { | ||
190 | struct drm_fb_cma *fb_cma = to_fb_cma(fb); | ||
191 | int i, n = drm_format_num_planes(fb->pixel_format); | ||
192 | |||
193 | seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, | ||
194 | (char *)&fb->pixel_format); | ||
195 | |||
196 | for (i = 0; i < n; i++) { | ||
197 | seq_printf(m, " %d: offset=%d pitch=%d, obj: ", | ||
198 | i, fb->offsets[i], fb->pitches[i]); | ||
199 | drm_gem_cma_describe(fb_cma->obj[i], m); | ||
200 | } | ||
201 | } | ||
202 | EXPORT_SYMBOL_GPL(drm_fb_cma_describe); | ||
203 | |||
204 | /** | ||
205 | * drm_fb_cma_debugfs_show() - Helper to list CMA framebuffer objects | ||
206 | * in debugfs. | ||
207 | */ | ||
208 | int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg) | ||
209 | { | ||
210 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
211 | struct drm_device *dev = node->minor->dev; | ||
212 | struct drm_framebuffer *fb; | ||
213 | int ret; | ||
214 | |||
215 | ret = mutex_lock_interruptible(&dev->mode_config.mutex); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | |||
219 | ret = mutex_lock_interruptible(&dev->struct_mutex); | ||
220 | if (ret) { | ||
221 | mutex_unlock(&dev->mode_config.mutex); | ||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | list_for_each_entry(fb, &dev->mode_config.fb_list, head) | ||
226 | drm_fb_cma_describe(fb, m); | ||
227 | |||
228 | mutex_unlock(&dev->struct_mutex); | ||
229 | mutex_unlock(&dev->mode_config.mutex); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | EXPORT_SYMBOL_GPL(drm_fb_cma_debugfs_show); | ||
234 | #endif | ||
235 | |||
183 | static struct fb_ops drm_fbdev_cma_ops = { | 236 | static struct fb_ops drm_fbdev_cma_ops = { |
184 | .owner = THIS_MODULE, | 237 | .owner = THIS_MODULE, |
185 | .fb_fillrect = sys_fillrect, | 238 | .fb_fillrect = sys_fillrect, |
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 1aa8fee1e865..0a7e011509bd 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c | |||
@@ -249,3 +249,24 @@ int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, | |||
249 | return drm_gem_handle_delete(file_priv, handle); | 249 | return drm_gem_handle_delete(file_priv, handle); |
250 | } | 250 | } |
251 | EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy); | 251 | EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy); |
252 | |||
253 | #ifdef CONFIG_DEBUG_FS | ||
254 | void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m) | ||
255 | { | ||
256 | struct drm_gem_object *obj = &cma_obj->base; | ||
257 | struct drm_device *dev = obj->dev; | ||
258 | uint64_t off = 0; | ||
259 | |||
260 | WARN_ON(!mutex_is_locked(&dev->struct_mutex)); | ||
261 | |||
262 | if (obj->map_list.map) | ||
263 | off = (uint64_t)obj->map_list.hash.key; | ||
264 | |||
265 | seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d", | ||
266 | obj->name, obj->refcount.refcount.counter, | ||
267 | off, cma_obj->paddr, cma_obj->vaddr, obj->size); | ||
268 | |||
269 | seq_printf(m, "\n"); | ||
270 | } | ||
271 | EXPORT_SYMBOL_GPL(drm_gem_cma_describe); | ||
272 | #endif | ||
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 38e79927b2d7..a6a8643a6a77 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -867,6 +867,7 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc, | |||
867 | 867 | ||
868 | now = get_drm_timestamp(); | 868 | now = get_drm_timestamp(); |
869 | } | 869 | } |
870 | e->pipe = crtc; | ||
870 | send_vblank_event(dev, e, seq, &now); | 871 | send_vblank_event(dev, e, seq, &now); |
871 | } | 872 | } |
872 | EXPORT_SYMBOL(drm_send_vblank_event); | 873 | EXPORT_SYMBOL(drm_send_vblank_event); |
diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 16118363509a..4d341db462a2 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig | |||
@@ -19,4 +19,10 @@ config DRM_I2C_SIL164 | |||
19 | when used in pairs) TMDS transmitters, used in some nVidia | 19 | when used in pairs) TMDS transmitters, used in some nVidia |
20 | video cards. | 20 | video cards. |
21 | 21 | ||
22 | config DRM_I2C_NXP_TDA998X | ||
23 | tristate "NXP Semiconductors TDA998X HDMI encoder" | ||
24 | default m if DRM_TILCDC | ||
25 | help | ||
26 | Support for NXP Semiconductors TDA998X HDMI encoders. | ||
27 | |||
22 | endmenu | 28 | endmenu |
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 92862563e7ee..43aa33baebed 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile | |||
@@ -5,3 +5,6 @@ obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o | |||
5 | 5 | ||
6 | sil164-y := sil164_drv.o | 6 | sil164-y := sil164_drv.o |
7 | obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o | 7 | obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o |
8 | |||
9 | tda998x-y := tda998x_drv.o | ||
10 | obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o | ||
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c new file mode 100644 index 000000000000..e68b58a1aaf9 --- /dev/null +++ b/drivers/gpu/drm/i2c/tda998x_drv.c | |||
@@ -0,0 +1,906 @@ | |||
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 | |||
19 | |||
20 | #include <linux/module.h> | ||
21 | |||
22 | #include <drm/drmP.h> | ||
23 | #include <drm/drm_crtc_helper.h> | ||
24 | #include <drm/drm_encoder_slave.h> | ||
25 | #include <drm/drm_edid.h> | ||
26 | |||
27 | |||
28 | #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) | ||
29 | |||
30 | struct tda998x_priv { | ||
31 | struct i2c_client *cec; | ||
32 | uint16_t rev; | ||
33 | uint8_t current_page; | ||
34 | int dpms; | ||
35 | }; | ||
36 | |||
37 | #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) | ||
38 | |||
39 | /* The TDA9988 series of devices use a paged register scheme.. to simplify | ||
40 | * things we encode the page # in upper bits of the register #. To read/ | ||
41 | * write a given register, we need to make sure CURPAGE register is set | ||
42 | * appropriately. Which implies reads/writes are not atomic. Fun! | ||
43 | */ | ||
44 | |||
45 | #define REG(page, addr) (((page) << 8) | (addr)) | ||
46 | #define REG2ADDR(reg) ((reg) & 0xff) | ||
47 | #define REG2PAGE(reg) (((reg) >> 8) & 0xff) | ||
48 | |||
49 | #define REG_CURPAGE 0xff /* write */ | ||
50 | |||
51 | |||
52 | /* Page 00h: General Control */ | ||
53 | #define REG_VERSION_LSB REG(0x00, 0x00) /* read */ | ||
54 | #define REG_MAIN_CNTRL0 REG(0x00, 0x01) /* read/write */ | ||
55 | # define MAIN_CNTRL0_SR (1 << 0) | ||
56 | # define MAIN_CNTRL0_DECS (1 << 1) | ||
57 | # define MAIN_CNTRL0_DEHS (1 << 2) | ||
58 | # define MAIN_CNTRL0_CECS (1 << 3) | ||
59 | # define MAIN_CNTRL0_CEHS (1 << 4) | ||
60 | # define MAIN_CNTRL0_SCALER (1 << 7) | ||
61 | #define REG_VERSION_MSB REG(0x00, 0x02) /* read */ | ||
62 | #define REG_SOFTRESET REG(0x00, 0x0a) /* write */ | ||
63 | # define SOFTRESET_AUDIO (1 << 0) | ||
64 | # define SOFTRESET_I2C_MASTER (1 << 1) | ||
65 | #define REG_DDC_DISABLE REG(0x00, 0x0b) /* read/write */ | ||
66 | #define REG_CCLK_ON REG(0x00, 0x0c) /* read/write */ | ||
67 | #define REG_I2C_MASTER REG(0x00, 0x0d) /* read/write */ | ||
68 | # define I2C_MASTER_DIS_MM (1 << 0) | ||
69 | # define I2C_MASTER_DIS_FILT (1 << 1) | ||
70 | # define I2C_MASTER_APP_STRT_LAT (1 << 2) | ||
71 | #define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ | ||
72 | #define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ | ||
73 | #define REG_INT_FLAGS_2 REG(0x00, 0x11) /* read/write */ | ||
74 | # define INT_FLAGS_2_EDID_BLK_RD (1 << 1) | ||
75 | #define REG_ENA_VP_0 REG(0x00, 0x18) /* read/write */ | ||
76 | #define REG_ENA_VP_1 REG(0x00, 0x19) /* read/write */ | ||
77 | #define REG_ENA_VP_2 REG(0x00, 0x1a) /* read/write */ | ||
78 | #define REG_ENA_AP REG(0x00, 0x1e) /* read/write */ | ||
79 | #define REG_VIP_CNTRL_0 REG(0x00, 0x20) /* write */ | ||
80 | # define VIP_CNTRL_0_MIRR_A (1 << 7) | ||
81 | # define VIP_CNTRL_0_SWAP_A(x) (((x) & 7) << 4) | ||
82 | # define VIP_CNTRL_0_MIRR_B (1 << 3) | ||
83 | # define VIP_CNTRL_0_SWAP_B(x) (((x) & 7) << 0) | ||
84 | #define REG_VIP_CNTRL_1 REG(0x00, 0x21) /* write */ | ||
85 | # define VIP_CNTRL_1_MIRR_C (1 << 7) | ||
86 | # define VIP_CNTRL_1_SWAP_C(x) (((x) & 7) << 4) | ||
87 | # define VIP_CNTRL_1_MIRR_D (1 << 3) | ||
88 | # define VIP_CNTRL_1_SWAP_D(x) (((x) & 7) << 0) | ||
89 | #define REG_VIP_CNTRL_2 REG(0x00, 0x22) /* write */ | ||
90 | # define VIP_CNTRL_2_MIRR_E (1 << 7) | ||
91 | # define VIP_CNTRL_2_SWAP_E(x) (((x) & 7) << 4) | ||
92 | # define VIP_CNTRL_2_MIRR_F (1 << 3) | ||
93 | # define VIP_CNTRL_2_SWAP_F(x) (((x) & 7) << 0) | ||
94 | #define REG_VIP_CNTRL_3 REG(0x00, 0x23) /* write */ | ||
95 | # define VIP_CNTRL_3_X_TGL (1 << 0) | ||
96 | # define VIP_CNTRL_3_H_TGL (1 << 1) | ||
97 | # define VIP_CNTRL_3_V_TGL (1 << 2) | ||
98 | # define VIP_CNTRL_3_EMB (1 << 3) | ||
99 | # define VIP_CNTRL_3_SYNC_DE (1 << 4) | ||
100 | # define VIP_CNTRL_3_SYNC_HS (1 << 5) | ||
101 | # define VIP_CNTRL_3_DE_INT (1 << 6) | ||
102 | # define VIP_CNTRL_3_EDGE (1 << 7) | ||
103 | #define REG_VIP_CNTRL_4 REG(0x00, 0x24) /* write */ | ||
104 | # define VIP_CNTRL_4_BLC(x) (((x) & 3) << 0) | ||
105 | # define VIP_CNTRL_4_BLANKIT(x) (((x) & 3) << 2) | ||
106 | # define VIP_CNTRL_4_CCIR656 (1 << 4) | ||
107 | # define VIP_CNTRL_4_656_ALT (1 << 5) | ||
108 | # define VIP_CNTRL_4_TST_656 (1 << 6) | ||
109 | # define VIP_CNTRL_4_TST_PAT (1 << 7) | ||
110 | #define REG_VIP_CNTRL_5 REG(0x00, 0x25) /* write */ | ||
111 | # define VIP_CNTRL_5_CKCASE (1 << 0) | ||
112 | # define VIP_CNTRL_5_SP_CNT(x) (((x) & 3) << 1) | ||
113 | #define REG_MAT_CONTRL REG(0x00, 0x80) /* write */ | ||
114 | # define MAT_CONTRL_MAT_SC(x) (((x) & 3) << 0) | ||
115 | # define MAT_CONTRL_MAT_BP (1 << 2) | ||
116 | #define REG_VIDFORMAT REG(0x00, 0xa0) /* write */ | ||
117 | #define REG_REFPIX_MSB REG(0x00, 0xa1) /* write */ | ||
118 | #define REG_REFPIX_LSB REG(0x00, 0xa2) /* write */ | ||
119 | #define REG_REFLINE_MSB REG(0x00, 0xa3) /* write */ | ||
120 | #define REG_REFLINE_LSB REG(0x00, 0xa4) /* write */ | ||
121 | #define REG_NPIX_MSB REG(0x00, 0xa5) /* write */ | ||
122 | #define REG_NPIX_LSB REG(0x00, 0xa6) /* write */ | ||
123 | #define REG_NLINE_MSB REG(0x00, 0xa7) /* write */ | ||
124 | #define REG_NLINE_LSB REG(0x00, 0xa8) /* write */ | ||
125 | #define REG_VS_LINE_STRT_1_MSB REG(0x00, 0xa9) /* write */ | ||
126 | #define REG_VS_LINE_STRT_1_LSB REG(0x00, 0xaa) /* write */ | ||
127 | #define REG_VS_PIX_STRT_1_MSB REG(0x00, 0xab) /* write */ | ||
128 | #define REG_VS_PIX_STRT_1_LSB REG(0x00, 0xac) /* write */ | ||
129 | #define REG_VS_LINE_END_1_MSB REG(0x00, 0xad) /* write */ | ||
130 | #define REG_VS_LINE_END_1_LSB REG(0x00, 0xae) /* write */ | ||
131 | #define REG_VS_PIX_END_1_MSB REG(0x00, 0xaf) /* write */ | ||
132 | #define REG_VS_PIX_END_1_LSB REG(0x00, 0xb0) /* write */ | ||
133 | #define REG_VS_PIX_STRT_2_MSB REG(0x00, 0xb3) /* write */ | ||
134 | #define REG_VS_PIX_STRT_2_LSB REG(0x00, 0xb4) /* write */ | ||
135 | #define REG_VS_PIX_END_2_MSB REG(0x00, 0xb7) /* write */ | ||
136 | #define REG_VS_PIX_END_2_LSB REG(0x00, 0xb8) /* write */ | ||
137 | #define REG_HS_PIX_START_MSB REG(0x00, 0xb9) /* write */ | ||
138 | #define REG_HS_PIX_START_LSB REG(0x00, 0xba) /* write */ | ||
139 | #define REG_HS_PIX_STOP_MSB REG(0x00, 0xbb) /* write */ | ||
140 | #define REG_HS_PIX_STOP_LSB REG(0x00, 0xbc) /* write */ | ||
141 | #define REG_VWIN_START_1_MSB REG(0x00, 0xbd) /* write */ | ||
142 | #define REG_VWIN_START_1_LSB REG(0x00, 0xbe) /* write */ | ||
143 | #define REG_VWIN_END_1_MSB REG(0x00, 0xbf) /* write */ | ||
144 | #define REG_VWIN_END_1_LSB REG(0x00, 0xc0) /* write */ | ||
145 | #define REG_DE_START_MSB REG(0x00, 0xc5) /* write */ | ||
146 | #define REG_DE_START_LSB REG(0x00, 0xc6) /* write */ | ||
147 | #define REG_DE_STOP_MSB REG(0x00, 0xc7) /* write */ | ||
148 | #define REG_DE_STOP_LSB REG(0x00, 0xc8) /* write */ | ||
149 | #define REG_TBG_CNTRL_0 REG(0x00, 0xca) /* write */ | ||
150 | # define TBG_CNTRL_0_FRAME_DIS (1 << 5) | ||
151 | # define TBG_CNTRL_0_SYNC_MTHD (1 << 6) | ||
152 | # define TBG_CNTRL_0_SYNC_ONCE (1 << 7) | ||
153 | #define REG_TBG_CNTRL_1 REG(0x00, 0xcb) /* write */ | ||
154 | # define TBG_CNTRL_1_VH_TGL_0 (1 << 0) | ||
155 | # define TBG_CNTRL_1_VH_TGL_1 (1 << 1) | ||
156 | # define TBG_CNTRL_1_VH_TGL_2 (1 << 2) | ||
157 | # define TBG_CNTRL_1_VHX_EXT_DE (1 << 3) | ||
158 | # define TBG_CNTRL_1_VHX_EXT_HS (1 << 4) | ||
159 | # define TBG_CNTRL_1_VHX_EXT_VS (1 << 5) | ||
160 | # define TBG_CNTRL_1_DWIN_DIS (1 << 6) | ||
161 | #define REG_ENABLE_SPACE REG(0x00, 0xd6) /* write */ | ||
162 | #define REG_HVF_CNTRL_0 REG(0x00, 0xe4) /* write */ | ||
163 | # define HVF_CNTRL_0_SM (1 << 7) | ||
164 | # define HVF_CNTRL_0_RWB (1 << 6) | ||
165 | # define HVF_CNTRL_0_PREFIL(x) (((x) & 3) << 2) | ||
166 | # define HVF_CNTRL_0_INTPOL(x) (((x) & 3) << 0) | ||
167 | #define REG_HVF_CNTRL_1 REG(0x00, 0xe5) /* write */ | ||
168 | # define HVF_CNTRL_1_FOR (1 << 0) | ||
169 | # define HVF_CNTRL_1_YUVBLK (1 << 1) | ||
170 | # define HVF_CNTRL_1_VQR(x) (((x) & 3) << 2) | ||
171 | # define HVF_CNTRL_1_PAD(x) (((x) & 3) << 4) | ||
172 | # define HVF_CNTRL_1_SEMI_PLANAR (1 << 6) | ||
173 | #define REG_RPT_CNTRL REG(0x00, 0xf0) /* write */ | ||
174 | |||
175 | |||
176 | /* Page 02h: PLL settings */ | ||
177 | #define REG_PLL_SERIAL_1 REG(0x02, 0x00) /* read/write */ | ||
178 | # define PLL_SERIAL_1_SRL_FDN (1 << 0) | ||
179 | # define PLL_SERIAL_1_SRL_IZ(x) (((x) & 3) << 1) | ||
180 | # define PLL_SERIAL_1_SRL_MAN_IZ (1 << 6) | ||
181 | #define REG_PLL_SERIAL_2 REG(0x02, 0x01) /* read/write */ | ||
182 | # define PLL_SERIAL_2_SRL_NOSC(x) (((x) & 3) << 0) | ||
183 | # define PLL_SERIAL_2_SRL_PR(x) (((x) & 0xf) << 4) | ||
184 | #define REG_PLL_SERIAL_3 REG(0x02, 0x02) /* read/write */ | ||
185 | # define PLL_SERIAL_3_SRL_CCIR (1 << 0) | ||
186 | # define PLL_SERIAL_3_SRL_DE (1 << 2) | ||
187 | # define PLL_SERIAL_3_SRL_PXIN_SEL (1 << 4) | ||
188 | #define REG_SERIALIZER REG(0x02, 0x03) /* read/write */ | ||
189 | #define REG_BUFFER_OUT REG(0x02, 0x04) /* read/write */ | ||
190 | #define REG_PLL_SCG1 REG(0x02, 0x05) /* read/write */ | ||
191 | #define REG_PLL_SCG2 REG(0x02, 0x06) /* read/write */ | ||
192 | #define REG_PLL_SCGN1 REG(0x02, 0x07) /* read/write */ | ||
193 | #define REG_PLL_SCGN2 REG(0x02, 0x08) /* read/write */ | ||
194 | #define REG_PLL_SCGR1 REG(0x02, 0x09) /* read/write */ | ||
195 | #define REG_PLL_SCGR2 REG(0x02, 0x0a) /* read/write */ | ||
196 | #define REG_AUDIO_DIV REG(0x02, 0x0e) /* read/write */ | ||
197 | #define REG_SEL_CLK REG(0x02, 0x11) /* read/write */ | ||
198 | # define SEL_CLK_SEL_CLK1 (1 << 0) | ||
199 | # define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1) | ||
200 | # define SEL_CLK_ENA_SC_CLK (1 << 3) | ||
201 | #define REG_ANA_GENERAL REG(0x02, 0x12) /* read/write */ | ||
202 | |||
203 | |||
204 | /* Page 09h: EDID Control */ | ||
205 | #define REG_EDID_DATA_0 REG(0x09, 0x00) /* read */ | ||
206 | /* next 127 successive registers are the EDID block */ | ||
207 | #define REG_EDID_CTRL REG(0x09, 0xfa) /* read/write */ | ||
208 | #define REG_DDC_ADDR REG(0x09, 0xfb) /* read/write */ | ||
209 | #define REG_DDC_OFFS REG(0x09, 0xfc) /* read/write */ | ||
210 | #define REG_DDC_SEGM_ADDR REG(0x09, 0xfd) /* read/write */ | ||
211 | #define REG_DDC_SEGM REG(0x09, 0xfe) /* read/write */ | ||
212 | |||
213 | |||
214 | /* Page 10h: information frames and packets */ | ||
215 | |||
216 | |||
217 | /* Page 11h: audio settings and content info packets */ | ||
218 | #define REG_AIP_CNTRL_0 REG(0x11, 0x00) /* read/write */ | ||
219 | # define AIP_CNTRL_0_RST_FIFO (1 << 0) | ||
220 | # define AIP_CNTRL_0_SWAP (1 << 1) | ||
221 | # define AIP_CNTRL_0_LAYOUT (1 << 2) | ||
222 | # define AIP_CNTRL_0_ACR_MAN (1 << 5) | ||
223 | # define AIP_CNTRL_0_RST_CTS (1 << 6) | ||
224 | #define REG_ENC_CNTRL REG(0x11, 0x0d) /* read/write */ | ||
225 | # define ENC_CNTRL_RST_ENC (1 << 0) | ||
226 | # define ENC_CNTRL_RST_SEL (1 << 1) | ||
227 | # define ENC_CNTRL_CTL_CODE(x) (((x) & 3) << 2) | ||
228 | |||
229 | |||
230 | /* Page 12h: HDCP and OTP */ | ||
231 | #define REG_TX3 REG(0x12, 0x9a) /* read/write */ | ||
232 | #define REG_TX33 REG(0x12, 0xb8) /* read/write */ | ||
233 | # define TX33_HDMI (1 << 1) | ||
234 | |||
235 | |||
236 | /* Page 13h: Gamut related metadata packets */ | ||
237 | |||
238 | |||
239 | |||
240 | /* CEC registers: (not paged) | ||
241 | */ | ||
242 | #define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */ | ||
243 | # define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7) | ||
244 | # define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6) | ||
245 | # define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1) | ||
246 | # define CEC_FRO_IM_CLK_CTRL_FRO_DIV (1 << 0) | ||
247 | #define REG_CEC_RXSHPDLEV 0xfe /* read */ | ||
248 | # define CEC_RXSHPDLEV_RXSENS (1 << 0) | ||
249 | # define CEC_RXSHPDLEV_HPD (1 << 1) | ||
250 | |||
251 | #define REG_CEC_ENAMODS 0xff /* read/write */ | ||
252 | # define CEC_ENAMODS_DIS_FRO (1 << 6) | ||
253 | # define CEC_ENAMODS_DIS_CCLK (1 << 5) | ||
254 | # define CEC_ENAMODS_EN_RXSENS (1 << 2) | ||
255 | # define CEC_ENAMODS_EN_HDMI (1 << 1) | ||
256 | # define CEC_ENAMODS_EN_CEC (1 << 0) | ||
257 | |||
258 | |||
259 | /* Device versions: */ | ||
260 | #define TDA9989N2 0x0101 | ||
261 | #define TDA19989 0x0201 | ||
262 | #define TDA19989N2 0x0202 | ||
263 | #define TDA19988 0x0301 | ||
264 | |||
265 | static void | ||
266 | cec_write(struct drm_encoder *encoder, uint16_t addr, uint8_t val) | ||
267 | { | ||
268 | struct i2c_client *client = to_tda998x_priv(encoder)->cec; | ||
269 | uint8_t buf[] = {addr, val}; | ||
270 | int ret; | ||
271 | |||
272 | ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); | ||
273 | if (ret < 0) | ||
274 | dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr); | ||
275 | } | ||
276 | |||
277 | static uint8_t | ||
278 | cec_read(struct drm_encoder *encoder, uint8_t addr) | ||
279 | { | ||
280 | struct i2c_client *client = to_tda998x_priv(encoder)->cec; | ||
281 | uint8_t val; | ||
282 | int ret; | ||
283 | |||
284 | ret = i2c_master_send(client, &addr, sizeof(addr)); | ||
285 | if (ret < 0) | ||
286 | goto fail; | ||
287 | |||
288 | ret = i2c_master_recv(client, &val, sizeof(val)); | ||
289 | if (ret < 0) | ||
290 | goto fail; | ||
291 | |||
292 | return val; | ||
293 | |||
294 | fail: | ||
295 | dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static void | ||
300 | set_page(struct drm_encoder *encoder, uint16_t reg) | ||
301 | { | ||
302 | struct tda998x_priv *priv = to_tda998x_priv(encoder); | ||
303 | |||
304 | if (REG2PAGE(reg) != priv->current_page) { | ||
305 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
306 | uint8_t buf[] = { | ||
307 | REG_CURPAGE, REG2PAGE(reg) | ||
308 | }; | ||
309 | int ret = i2c_master_send(client, buf, sizeof(buf)); | ||
310 | if (ret < 0) | ||
311 | dev_err(&client->dev, "Error %d writing to REG_CURPAGE\n", ret); | ||
312 | |||
313 | priv->current_page = REG2PAGE(reg); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | static int | ||
318 | reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt) | ||
319 | { | ||
320 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
321 | uint8_t addr = REG2ADDR(reg); | ||
322 | int ret; | ||
323 | |||
324 | set_page(encoder, reg); | ||
325 | |||
326 | ret = i2c_master_send(client, &addr, sizeof(addr)); | ||
327 | if (ret < 0) | ||
328 | goto fail; | ||
329 | |||
330 | ret = i2c_master_recv(client, buf, cnt); | ||
331 | if (ret < 0) | ||
332 | goto fail; | ||
333 | |||
334 | return ret; | ||
335 | |||
336 | fail: | ||
337 | dev_err(&client->dev, "Error %d reading from 0x%x\n", ret, reg); | ||
338 | return ret; | ||
339 | } | ||
340 | |||
341 | static uint8_t | ||
342 | reg_read(struct drm_encoder *encoder, uint16_t reg) | ||
343 | { | ||
344 | uint8_t val = 0; | ||
345 | reg_read_range(encoder, reg, &val, sizeof(val)); | ||
346 | return val; | ||
347 | } | ||
348 | |||
349 | static void | ||
350 | reg_write(struct drm_encoder *encoder, uint16_t reg, uint8_t val) | ||
351 | { | ||
352 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
353 | uint8_t buf[] = {REG2ADDR(reg), val}; | ||
354 | int ret; | ||
355 | |||
356 | set_page(encoder, reg); | ||
357 | |||
358 | ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); | ||
359 | if (ret < 0) | ||
360 | dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); | ||
361 | } | ||
362 | |||
363 | static void | ||
364 | reg_write16(struct drm_encoder *encoder, uint16_t reg, uint16_t val) | ||
365 | { | ||
366 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
367 | uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; | ||
368 | int ret; | ||
369 | |||
370 | set_page(encoder, reg); | ||
371 | |||
372 | ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); | ||
373 | if (ret < 0) | ||
374 | dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); | ||
375 | } | ||
376 | |||
377 | static void | ||
378 | reg_set(struct drm_encoder *encoder, uint16_t reg, uint8_t val) | ||
379 | { | ||
380 | reg_write(encoder, reg, reg_read(encoder, reg) | val); | ||
381 | } | ||
382 | |||
383 | static void | ||
384 | reg_clear(struct drm_encoder *encoder, uint16_t reg, uint8_t val) | ||
385 | { | ||
386 | reg_write(encoder, reg, reg_read(encoder, reg) & ~val); | ||
387 | } | ||
388 | |||
389 | static void | ||
390 | tda998x_reset(struct drm_encoder *encoder) | ||
391 | { | ||
392 | /* reset audio and i2c master: */ | ||
393 | reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER); | ||
394 | msleep(50); | ||
395 | reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER); | ||
396 | msleep(50); | ||
397 | |||
398 | /* reset transmitter: */ | ||
399 | reg_set(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); | ||
400 | reg_clear(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); | ||
401 | |||
402 | /* PLL registers common configuration */ | ||
403 | reg_write(encoder, REG_PLL_SERIAL_1, 0x00); | ||
404 | reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1)); | ||
405 | reg_write(encoder, REG_PLL_SERIAL_3, 0x00); | ||
406 | reg_write(encoder, REG_SERIALIZER, 0x00); | ||
407 | reg_write(encoder, REG_BUFFER_OUT, 0x00); | ||
408 | reg_write(encoder, REG_PLL_SCG1, 0x00); | ||
409 | reg_write(encoder, REG_AUDIO_DIV, 0x03); | ||
410 | reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); | ||
411 | reg_write(encoder, REG_PLL_SCGN1, 0xfa); | ||
412 | reg_write(encoder, REG_PLL_SCGN2, 0x00); | ||
413 | reg_write(encoder, REG_PLL_SCGR1, 0x5b); | ||
414 | reg_write(encoder, REG_PLL_SCGR2, 0x00); | ||
415 | reg_write(encoder, REG_PLL_SCG2, 0x10); | ||
416 | } | ||
417 | |||
418 | /* DRM encoder functions */ | ||
419 | |||
420 | static void | ||
421 | tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) | ||
422 | { | ||
423 | } | ||
424 | |||
425 | static void | ||
426 | tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
427 | { | ||
428 | struct tda998x_priv *priv = to_tda998x_priv(encoder); | ||
429 | |||
430 | /* we only care about on or off: */ | ||
431 | if (mode != DRM_MODE_DPMS_ON) | ||
432 | mode = DRM_MODE_DPMS_OFF; | ||
433 | |||
434 | if (mode == priv->dpms) | ||
435 | return; | ||
436 | |||
437 | switch (mode) { | ||
438 | case DRM_MODE_DPMS_ON: | ||
439 | /* enable audio and video ports */ | ||
440 | reg_write(encoder, REG_ENA_AP, 0xff); | ||
441 | reg_write(encoder, REG_ENA_VP_0, 0xff); | ||
442 | reg_write(encoder, REG_ENA_VP_1, 0xff); | ||
443 | reg_write(encoder, REG_ENA_VP_2, 0xff); | ||
444 | /* set muxing after enabling ports: */ | ||
445 | reg_write(encoder, REG_VIP_CNTRL_0, | ||
446 | VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3)); | ||
447 | reg_write(encoder, REG_VIP_CNTRL_1, | ||
448 | VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1)); | ||
449 | reg_write(encoder, REG_VIP_CNTRL_2, | ||
450 | VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5)); | ||
451 | break; | ||
452 | case DRM_MODE_DPMS_OFF: | ||
453 | /* disable audio and video ports */ | ||
454 | reg_write(encoder, REG_ENA_AP, 0x00); | ||
455 | reg_write(encoder, REG_ENA_VP_0, 0x00); | ||
456 | reg_write(encoder, REG_ENA_VP_1, 0x00); | ||
457 | reg_write(encoder, REG_ENA_VP_2, 0x00); | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | priv->dpms = mode; | ||
462 | } | ||
463 | |||
464 | static void | ||
465 | tda998x_encoder_save(struct drm_encoder *encoder) | ||
466 | { | ||
467 | DBG(""); | ||
468 | } | ||
469 | |||
470 | static void | ||
471 | tda998x_encoder_restore(struct drm_encoder *encoder) | ||
472 | { | ||
473 | DBG(""); | ||
474 | } | ||
475 | |||
476 | static bool | ||
477 | tda998x_encoder_mode_fixup(struct drm_encoder *encoder, | ||
478 | const struct drm_display_mode *mode, | ||
479 | struct drm_display_mode *adjusted_mode) | ||
480 | { | ||
481 | return true; | ||
482 | } | ||
483 | |||
484 | static int | ||
485 | tda998x_encoder_mode_valid(struct drm_encoder *encoder, | ||
486 | struct drm_display_mode *mode) | ||
487 | { | ||
488 | return MODE_OK; | ||
489 | } | ||
490 | |||
491 | static void | ||
492 | tda998x_encoder_mode_set(struct drm_encoder *encoder, | ||
493 | struct drm_display_mode *mode, | ||
494 | struct drm_display_mode *adjusted_mode) | ||
495 | { | ||
496 | struct tda998x_priv *priv = to_tda998x_priv(encoder); | ||
497 | uint16_t hs_start, hs_end, line_start, line_end; | ||
498 | uint16_t vwin_start, vwin_end, de_start, de_end; | ||
499 | uint16_t ref_pix, ref_line, pix_start2; | ||
500 | uint8_t reg, div, rep; | ||
501 | |||
502 | hs_start = mode->hsync_start - mode->hdisplay; | ||
503 | hs_end = mode->hsync_end - mode->hdisplay; | ||
504 | line_start = 1; | ||
505 | line_end = 1 + mode->vsync_end - mode->vsync_start; | ||
506 | vwin_start = mode->vtotal - mode->vsync_start; | ||
507 | vwin_end = vwin_start + mode->vdisplay; | ||
508 | de_start = mode->htotal - mode->hdisplay; | ||
509 | de_end = mode->htotal; | ||
510 | |||
511 | pix_start2 = 0; | ||
512 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
513 | pix_start2 = (mode->htotal / 2) + hs_start; | ||
514 | |||
515 | /* TODO how is this value calculated? It is 2 for all common | ||
516 | * formats in the tables in out of tree nxp driver (assuming | ||
517 | * I've properly deciphered their byzantine table system) | ||
518 | */ | ||
519 | ref_line = 2; | ||
520 | |||
521 | /* this might changes for other color formats from the CRTC: */ | ||
522 | ref_pix = 3 + hs_start; | ||
523 | |||
524 | div = 148500 / mode->clock; | ||
525 | |||
526 | DBG("clock=%d, div=%u", mode->clock, div); | ||
527 | DBG("hs_start=%u, hs_end=%u, line_start=%u, line_end=%u", | ||
528 | hs_start, hs_end, line_start, line_end); | ||
529 | DBG("vwin_start=%u, vwin_end=%u, de_start=%u, de_end=%u", | ||
530 | vwin_start, vwin_end, de_start, de_end); | ||
531 | DBG("ref_line=%u, ref_pix=%u, pix_start2=%u", | ||
532 | ref_line, ref_pix, pix_start2); | ||
533 | |||
534 | /* mute the audio FIFO: */ | ||
535 | reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); | ||
536 | |||
537 | /* set HDMI HDCP mode off: */ | ||
538 | reg_set(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); | ||
539 | reg_clear(encoder, REG_TX33, TX33_HDMI); | ||
540 | |||
541 | reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0)); | ||
542 | /* no pre-filter or interpolator: */ | ||
543 | reg_write(encoder, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) | | ||
544 | HVF_CNTRL_0_INTPOL(0)); | ||
545 | reg_write(encoder, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0)); | ||
546 | reg_write(encoder, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) | | ||
547 | VIP_CNTRL_4_BLC(0)); | ||
548 | reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR); | ||
549 | |||
550 | reg_clear(encoder, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ); | ||
551 | reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE); | ||
552 | reg_write(encoder, REG_SERIALIZER, 0); | ||
553 | reg_write(encoder, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0)); | ||
554 | |||
555 | /* TODO enable pixel repeat for pixel rates less than 25Msamp/s */ | ||
556 | rep = 0; | ||
557 | reg_write(encoder, REG_RPT_CNTRL, 0); | ||
558 | reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | | ||
559 | SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); | ||
560 | |||
561 | reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) | | ||
562 | PLL_SERIAL_2_SRL_PR(rep)); | ||
563 | |||
564 | reg_write16(encoder, REG_VS_PIX_STRT_2_MSB, pix_start2); | ||
565 | reg_write16(encoder, REG_VS_PIX_END_2_MSB, pix_start2); | ||
566 | |||
567 | /* set color matrix bypass flag: */ | ||
568 | reg_set(encoder, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP); | ||
569 | |||
570 | /* set BIAS tmds value: */ | ||
571 | reg_write(encoder, REG_ANA_GENERAL, 0x09); | ||
572 | |||
573 | reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD); | ||
574 | |||
575 | reg_write(encoder, REG_VIP_CNTRL_3, 0); | ||
576 | reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_SYNC_HS); | ||
577 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
578 | reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_V_TGL); | ||
579 | |||
580 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
581 | reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_H_TGL); | ||
582 | |||
583 | reg_write(encoder, REG_VIDFORMAT, 0x00); | ||
584 | reg_write16(encoder, REG_NPIX_MSB, mode->hdisplay - 1); | ||
585 | reg_write16(encoder, REG_NLINE_MSB, mode->vdisplay - 1); | ||
586 | reg_write16(encoder, REG_VS_LINE_STRT_1_MSB, line_start); | ||
587 | reg_write16(encoder, REG_VS_LINE_END_1_MSB, line_end); | ||
588 | reg_write16(encoder, REG_VS_PIX_STRT_1_MSB, hs_start); | ||
589 | reg_write16(encoder, REG_VS_PIX_END_1_MSB, hs_start); | ||
590 | reg_write16(encoder, REG_HS_PIX_START_MSB, hs_start); | ||
591 | reg_write16(encoder, REG_HS_PIX_STOP_MSB, hs_end); | ||
592 | reg_write16(encoder, REG_VWIN_START_1_MSB, vwin_start); | ||
593 | reg_write16(encoder, REG_VWIN_END_1_MSB, vwin_end); | ||
594 | reg_write16(encoder, REG_DE_START_MSB, de_start); | ||
595 | reg_write16(encoder, REG_DE_STOP_MSB, de_end); | ||
596 | |||
597 | if (priv->rev == TDA19988) { | ||
598 | /* let incoming pixels fill the active space (if any) */ | ||
599 | reg_write(encoder, REG_ENABLE_SPACE, 0x01); | ||
600 | } | ||
601 | |||
602 | reg_write16(encoder, REG_REFPIX_MSB, ref_pix); | ||
603 | reg_write16(encoder, REG_REFLINE_MSB, ref_line); | ||
604 | |||
605 | reg = TBG_CNTRL_1_VHX_EXT_DE | | ||
606 | TBG_CNTRL_1_VHX_EXT_HS | | ||
607 | TBG_CNTRL_1_VHX_EXT_VS | | ||
608 | TBG_CNTRL_1_DWIN_DIS | /* HDCP off */ | ||
609 | TBG_CNTRL_1_VH_TGL_2; | ||
610 | if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC)) | ||
611 | reg |= TBG_CNTRL_1_VH_TGL_0; | ||
612 | reg_set(encoder, REG_TBG_CNTRL_1, reg); | ||
613 | |||
614 | /* must be last register set: */ | ||
615 | reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE); | ||
616 | } | ||
617 | |||
618 | static enum drm_connector_status | ||
619 | tda998x_encoder_detect(struct drm_encoder *encoder, | ||
620 | struct drm_connector *connector) | ||
621 | { | ||
622 | uint8_t val = cec_read(encoder, REG_CEC_RXSHPDLEV); | ||
623 | return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : | ||
624 | connector_status_disconnected; | ||
625 | } | ||
626 | |||
627 | static int | ||
628 | read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) | ||
629 | { | ||
630 | uint8_t offset, segptr; | ||
631 | int ret, i; | ||
632 | |||
633 | /* enable EDID read irq: */ | ||
634 | reg_set(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); | ||
635 | |||
636 | offset = (blk & 1) ? 128 : 0; | ||
637 | segptr = blk / 2; | ||
638 | |||
639 | reg_write(encoder, REG_DDC_ADDR, 0xa0); | ||
640 | reg_write(encoder, REG_DDC_OFFS, offset); | ||
641 | reg_write(encoder, REG_DDC_SEGM_ADDR, 0x60); | ||
642 | reg_write(encoder, REG_DDC_SEGM, segptr); | ||
643 | |||
644 | /* enable reading EDID: */ | ||
645 | reg_write(encoder, REG_EDID_CTRL, 0x1); | ||
646 | |||
647 | /* flag must be cleared by sw: */ | ||
648 | reg_write(encoder, REG_EDID_CTRL, 0x0); | ||
649 | |||
650 | /* wait for block read to complete: */ | ||
651 | for (i = 100; i > 0; i--) { | ||
652 | uint8_t val = reg_read(encoder, REG_INT_FLAGS_2); | ||
653 | if (val & INT_FLAGS_2_EDID_BLK_RD) | ||
654 | break; | ||
655 | msleep(1); | ||
656 | } | ||
657 | |||
658 | if (i == 0) | ||
659 | return -ETIMEDOUT; | ||
660 | |||
661 | ret = reg_read_range(encoder, REG_EDID_DATA_0, buf, EDID_LENGTH); | ||
662 | if (ret != EDID_LENGTH) { | ||
663 | dev_err(encoder->dev->dev, "failed to read edid block %d: %d", | ||
664 | blk, ret); | ||
665 | return ret; | ||
666 | } | ||
667 | |||
668 | reg_clear(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static uint8_t * | ||
674 | do_get_edid(struct drm_encoder *encoder) | ||
675 | { | ||
676 | int j = 0, valid_extensions = 0; | ||
677 | uint8_t *block, *new; | ||
678 | bool print_bad_edid = drm_debug & DRM_UT_KMS; | ||
679 | |||
680 | if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) | ||
681 | return NULL; | ||
682 | |||
683 | /* base block fetch */ | ||
684 | if (read_edid_block(encoder, block, 0)) | ||
685 | goto fail; | ||
686 | |||
687 | if (!drm_edid_block_valid(block, 0, print_bad_edid)) | ||
688 | goto fail; | ||
689 | |||
690 | /* if there's no extensions, we're done */ | ||
691 | if (block[0x7e] == 0) | ||
692 | return block; | ||
693 | |||
694 | new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); | ||
695 | if (!new) | ||
696 | goto fail; | ||
697 | block = new; | ||
698 | |||
699 | for (j = 1; j <= block[0x7e]; j++) { | ||
700 | uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH; | ||
701 | if (read_edid_block(encoder, ext_block, j)) | ||
702 | goto fail; | ||
703 | |||
704 | if (!drm_edid_block_valid(ext_block, j, print_bad_edid)) | ||
705 | goto fail; | ||
706 | |||
707 | valid_extensions++; | ||
708 | } | ||
709 | |||
710 | if (valid_extensions != block[0x7e]) { | ||
711 | block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; | ||
712 | block[0x7e] = valid_extensions; | ||
713 | new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); | ||
714 | if (!new) | ||
715 | goto fail; | ||
716 | block = new; | ||
717 | } | ||
718 | |||
719 | return block; | ||
720 | |||
721 | fail: | ||
722 | dev_warn(encoder->dev->dev, "failed to read EDID\n"); | ||
723 | kfree(block); | ||
724 | return NULL; | ||
725 | } | ||
726 | |||
727 | static int | ||
728 | tda998x_encoder_get_modes(struct drm_encoder *encoder, | ||
729 | struct drm_connector *connector) | ||
730 | { | ||
731 | struct edid *edid = (struct edid *)do_get_edid(encoder); | ||
732 | int n = 0; | ||
733 | |||
734 | if (edid) { | ||
735 | drm_mode_connector_update_edid_property(connector, edid); | ||
736 | n = drm_add_edid_modes(connector, edid); | ||
737 | kfree(edid); | ||
738 | } | ||
739 | |||
740 | return n; | ||
741 | } | ||
742 | |||
743 | static int | ||
744 | tda998x_encoder_create_resources(struct drm_encoder *encoder, | ||
745 | struct drm_connector *connector) | ||
746 | { | ||
747 | DBG(""); | ||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | static int | ||
752 | tda998x_encoder_set_property(struct drm_encoder *encoder, | ||
753 | struct drm_connector *connector, | ||
754 | struct drm_property *property, | ||
755 | uint64_t val) | ||
756 | { | ||
757 | DBG(""); | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static void | ||
762 | tda998x_encoder_destroy(struct drm_encoder *encoder) | ||
763 | { | ||
764 | struct tda998x_priv *priv = to_tda998x_priv(encoder); | ||
765 | drm_i2c_encoder_destroy(encoder); | ||
766 | kfree(priv); | ||
767 | } | ||
768 | |||
769 | static struct drm_encoder_slave_funcs tda998x_encoder_funcs = { | ||
770 | .set_config = tda998x_encoder_set_config, | ||
771 | .destroy = tda998x_encoder_destroy, | ||
772 | .dpms = tda998x_encoder_dpms, | ||
773 | .save = tda998x_encoder_save, | ||
774 | .restore = tda998x_encoder_restore, | ||
775 | .mode_fixup = tda998x_encoder_mode_fixup, | ||
776 | .mode_valid = tda998x_encoder_mode_valid, | ||
777 | .mode_set = tda998x_encoder_mode_set, | ||
778 | .detect = tda998x_encoder_detect, | ||
779 | .get_modes = tda998x_encoder_get_modes, | ||
780 | .create_resources = tda998x_encoder_create_resources, | ||
781 | .set_property = tda998x_encoder_set_property, | ||
782 | }; | ||
783 | |||
784 | /* I2C driver functions */ | ||
785 | |||
786 | static int | ||
787 | tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) | ||
788 | { | ||
789 | return 0; | ||
790 | } | ||
791 | |||
792 | static int | ||
793 | tda998x_remove(struct i2c_client *client) | ||
794 | { | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | static int | ||
799 | tda998x_encoder_init(struct i2c_client *client, | ||
800 | struct drm_device *dev, | ||
801 | struct drm_encoder_slave *encoder_slave) | ||
802 | { | ||
803 | struct drm_encoder *encoder = &encoder_slave->base; | ||
804 | struct tda998x_priv *priv; | ||
805 | |||
806 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
807 | if (!priv) | ||
808 | return -ENOMEM; | ||
809 | |||
810 | priv->current_page = 0; | ||
811 | priv->cec = i2c_new_dummy(client->adapter, 0x34); | ||
812 | priv->dpms = DRM_MODE_DPMS_OFF; | ||
813 | |||
814 | encoder_slave->slave_priv = priv; | ||
815 | encoder_slave->slave_funcs = &tda998x_encoder_funcs; | ||
816 | |||
817 | /* wake up the device: */ | ||
818 | cec_write(encoder, REG_CEC_ENAMODS, | ||
819 | CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); | ||
820 | |||
821 | tda998x_reset(encoder); | ||
822 | |||
823 | /* read version: */ | ||
824 | priv->rev = reg_read(encoder, REG_VERSION_LSB) | | ||
825 | reg_read(encoder, REG_VERSION_MSB) << 8; | ||
826 | |||
827 | /* mask off feature bits: */ | ||
828 | priv->rev &= ~0x30; /* not-hdcp and not-scalar bit */ | ||
829 | |||
830 | switch (priv->rev) { | ||
831 | case TDA9989N2: dev_info(dev->dev, "found TDA9989 n2"); break; | ||
832 | case TDA19989: dev_info(dev->dev, "found TDA19989"); break; | ||
833 | case TDA19989N2: dev_info(dev->dev, "found TDA19989 n2"); break; | ||
834 | case TDA19988: dev_info(dev->dev, "found TDA19988"); break; | ||
835 | default: | ||
836 | DBG("found unsupported device: %04x", priv->rev); | ||
837 | goto fail; | ||
838 | } | ||
839 | |||
840 | /* after reset, enable DDC: */ | ||
841 | reg_write(encoder, REG_DDC_DISABLE, 0x00); | ||
842 | |||
843 | /* set clock on DDC channel: */ | ||
844 | reg_write(encoder, REG_TX3, 39); | ||
845 | |||
846 | /* if necessary, disable multi-master: */ | ||
847 | if (priv->rev == TDA19989) | ||
848 | reg_set(encoder, REG_I2C_MASTER, I2C_MASTER_DIS_MM); | ||
849 | |||
850 | cec_write(encoder, REG_CEC_FRO_IM_CLK_CTRL, | ||
851 | CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL); | ||
852 | |||
853 | return 0; | ||
854 | |||
855 | fail: | ||
856 | /* if encoder_init fails, the encoder slave is never registered, | ||
857 | * so cleanup here: | ||
858 | */ | ||
859 | if (priv->cec) | ||
860 | i2c_unregister_device(priv->cec); | ||
861 | kfree(priv); | ||
862 | encoder_slave->slave_priv = NULL; | ||
863 | encoder_slave->slave_funcs = NULL; | ||
864 | return -ENXIO; | ||
865 | } | ||
866 | |||
867 | static struct i2c_device_id tda998x_ids[] = { | ||
868 | { "tda998x", 0 }, | ||
869 | { } | ||
870 | }; | ||
871 | MODULE_DEVICE_TABLE(i2c, tda998x_ids); | ||
872 | |||
873 | static struct drm_i2c_encoder_driver tda998x_driver = { | ||
874 | .i2c_driver = { | ||
875 | .probe = tda998x_probe, | ||
876 | .remove = tda998x_remove, | ||
877 | .driver = { | ||
878 | .name = "tda998x", | ||
879 | }, | ||
880 | .id_table = tda998x_ids, | ||
881 | }, | ||
882 | .encoder_init = tda998x_encoder_init, | ||
883 | }; | ||
884 | |||
885 | /* Module initialization */ | ||
886 | |||
887 | static int __init | ||
888 | tda998x_init(void) | ||
889 | { | ||
890 | DBG(""); | ||
891 | return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver); | ||
892 | } | ||
893 | |||
894 | static void __exit | ||
895 | tda998x_exit(void) | ||
896 | { | ||
897 | DBG(""); | ||
898 | drm_i2c_encoder_unregister(&tda998x_driver); | ||
899 | } | ||
900 | |||
901 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com"); | ||
902 | MODULE_DESCRIPTION("NXP Semiconductors TDA998X HDMI Encoder"); | ||
903 | MODULE_LICENSE("GPL"); | ||
904 | |||
905 | module_init(tda998x_init); | ||
906 | module_exit(tda998x_exit); | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c index 62e826a139b3..4a69ccdef9b4 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c | |||
@@ -184,14 +184,23 @@ static const struct drm_encoder_funcs nv04_tv_funcs = { | |||
184 | .destroy = nv04_tv_destroy, | 184 | .destroy = nv04_tv_destroy, |
185 | }; | 185 | }; |
186 | 186 | ||
187 | static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { | ||
188 | .dpms = nv04_tv_dpms, | ||
189 | .save = drm_i2c_encoder_save, | ||
190 | .restore = drm_i2c_encoder_restore, | ||
191 | .mode_fixup = drm_i2c_encoder_mode_fixup, | ||
192 | .prepare = nv04_tv_prepare, | ||
193 | .commit = nv04_tv_commit, | ||
194 | .mode_set = nv04_tv_mode_set, | ||
195 | .detect = drm_i2c_encoder_detect, | ||
196 | }; | ||
197 | |||
187 | int | 198 | int |
188 | nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) | 199 | nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) |
189 | { | 200 | { |
190 | struct nouveau_encoder *nv_encoder; | 201 | struct nouveau_encoder *nv_encoder; |
191 | struct drm_encoder *encoder; | 202 | struct drm_encoder *encoder; |
192 | struct drm_device *dev = connector->dev; | 203 | struct drm_device *dev = connector->dev; |
193 | struct drm_encoder_helper_funcs *hfuncs; | ||
194 | struct drm_encoder_slave_funcs *sfuncs; | ||
195 | struct nouveau_drm *drm = nouveau_drm(dev); | 204 | struct nouveau_drm *drm = nouveau_drm(dev); |
196 | struct nouveau_i2c *i2c = nouveau_i2c(drm->device); | 205 | struct nouveau_i2c *i2c = nouveau_i2c(drm->device); |
197 | struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); | 206 | struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); |
@@ -207,17 +216,11 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) | |||
207 | if (!nv_encoder) | 216 | if (!nv_encoder) |
208 | return -ENOMEM; | 217 | return -ENOMEM; |
209 | 218 | ||
210 | hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL); | ||
211 | if (!hfuncs) { | ||
212 | ret = -ENOMEM; | ||
213 | goto fail_free; | ||
214 | } | ||
215 | |||
216 | /* Initialize the common members */ | 219 | /* Initialize the common members */ |
217 | encoder = to_drm_encoder(nv_encoder); | 220 | encoder = to_drm_encoder(nv_encoder); |
218 | 221 | ||
219 | drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); | 222 | drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); |
220 | drm_encoder_helper_add(encoder, hfuncs); | 223 | drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); |
221 | 224 | ||
222 | encoder->possible_crtcs = entry->heads; | 225 | encoder->possible_crtcs = entry->heads; |
223 | encoder->possible_clones = 0; | 226 | encoder->possible_clones = 0; |
@@ -230,30 +233,14 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) | |||
230 | if (ret < 0) | 233 | if (ret < 0) |
231 | goto fail_cleanup; | 234 | goto fail_cleanup; |
232 | 235 | ||
233 | /* Fill the function pointers */ | ||
234 | sfuncs = get_slave_funcs(encoder); | ||
235 | |||
236 | *hfuncs = (struct drm_encoder_helper_funcs) { | ||
237 | .dpms = nv04_tv_dpms, | ||
238 | .save = sfuncs->save, | ||
239 | .restore = sfuncs->restore, | ||
240 | .mode_fixup = sfuncs->mode_fixup, | ||
241 | .prepare = nv04_tv_prepare, | ||
242 | .commit = nv04_tv_commit, | ||
243 | .mode_set = nv04_tv_mode_set, | ||
244 | .detect = sfuncs->detect, | ||
245 | }; | ||
246 | |||
247 | /* Attach it to the specified connector. */ | 236 | /* Attach it to the specified connector. */ |
248 | sfuncs->create_resources(encoder, connector); | 237 | get_slave_funcs(encoder)->create_resources(encoder, connector); |
249 | drm_mode_connector_attach_encoder(connector, encoder); | 238 | drm_mode_connector_attach_encoder(connector, encoder); |
250 | 239 | ||
251 | return 0; | 240 | return 0; |
252 | 241 | ||
253 | fail_cleanup: | 242 | fail_cleanup: |
254 | drm_encoder_cleanup(encoder); | 243 | drm_encoder_cleanup(encoder); |
255 | kfree(hfuncs); | ||
256 | fail_free: | ||
257 | kfree(nv_encoder); | 244 | kfree(nv_encoder); |
258 | return ret; | 245 | return ret; |
259 | } | 246 | } |
diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig new file mode 100644 index 000000000000..ae14fd6ea924 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Kconfig | |||
@@ -0,0 +1,13 @@ | |||
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 | select OF_VIDEOMODE | ||
8 | select OF_DISPLAY_TIMING | ||
9 | select BACKLIGHT_CLASS_DEVICE | ||
10 | help | ||
11 | Choose this option if you have an TI SoC with LCDC display | ||
12 | controller, for example AM33xx in beagle-bone, DA8xx, or | ||
13 | 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..deda656b10e7 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | ccflags-y := -Iinclude/drm -Werror | ||
2 | |||
3 | tilcdc-y := \ | ||
4 | tilcdc_crtc.o \ | ||
5 | tilcdc_tfp410.o \ | ||
6 | tilcdc_slave.o \ | ||
7 | tilcdc_panel.o \ | ||
8 | tilcdc_drv.o | ||
9 | |||
10 | 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..c5b592dc1970 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c | |||
@@ -0,0 +1,611 @@ | |||
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 | #include "tilcdc_slave.h" | ||
24 | #include "tilcdc_panel.h" | ||
25 | |||
26 | #include "drm_fb_helper.h" | ||
27 | |||
28 | static LIST_HEAD(module_list); | ||
29 | |||
30 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, | ||
31 | const struct tilcdc_module_ops *funcs) | ||
32 | { | ||
33 | mod->name = name; | ||
34 | mod->funcs = funcs; | ||
35 | INIT_LIST_HEAD(&mod->list); | ||
36 | list_add(&mod->list, &module_list); | ||
37 | } | ||
38 | |||
39 | void tilcdc_module_cleanup(struct tilcdc_module *mod) | ||
40 | { | ||
41 | list_del(&mod->list); | ||
42 | } | ||
43 | |||
44 | static struct of_device_id tilcdc_of_match[]; | ||
45 | |||
46 | static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, | ||
47 | struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) | ||
48 | { | ||
49 | return drm_fb_cma_create(dev, file_priv, mode_cmd); | ||
50 | } | ||
51 | |||
52 | static void tilcdc_fb_output_poll_changed(struct drm_device *dev) | ||
53 | { | ||
54 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
55 | if (priv->fbdev) | ||
56 | drm_fbdev_cma_hotplug_event(priv->fbdev); | ||
57 | } | ||
58 | |||
59 | static const struct drm_mode_config_funcs mode_config_funcs = { | ||
60 | .fb_create = tilcdc_fb_create, | ||
61 | .output_poll_changed = tilcdc_fb_output_poll_changed, | ||
62 | }; | ||
63 | |||
64 | static int modeset_init(struct drm_device *dev) | ||
65 | { | ||
66 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
67 | struct tilcdc_module *mod; | ||
68 | |||
69 | drm_mode_config_init(dev); | ||
70 | |||
71 | priv->crtc = tilcdc_crtc_create(dev); | ||
72 | |||
73 | list_for_each_entry(mod, &module_list, list) { | ||
74 | DBG("loading module: %s", mod->name); | ||
75 | mod->funcs->modeset_init(mod, dev); | ||
76 | } | ||
77 | |||
78 | if ((priv->num_encoders = 0) || (priv->num_connectors == 0)) { | ||
79 | /* oh nos! */ | ||
80 | dev_err(dev->dev, "no encoders/connectors found\n"); | ||
81 | return -ENXIO; | ||
82 | } | ||
83 | |||
84 | dev->mode_config.min_width = 0; | ||
85 | dev->mode_config.min_height = 0; | ||
86 | dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); | ||
87 | dev->mode_config.max_height = 2048; | ||
88 | dev->mode_config.funcs = &mode_config_funcs; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | #ifdef CONFIG_CPU_FREQ | ||
94 | static int cpufreq_transition(struct notifier_block *nb, | ||
95 | unsigned long val, void *data) | ||
96 | { | ||
97 | struct tilcdc_drm_private *priv = container_of(nb, | ||
98 | struct tilcdc_drm_private, freq_transition); | ||
99 | if (val == CPUFREQ_POSTCHANGE) { | ||
100 | if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) { | ||
101 | priv->lcd_fck_rate = clk_get_rate(priv->clk); | ||
102 | tilcdc_crtc_update_clk(priv->crtc); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | #endif | ||
109 | |||
110 | /* | ||
111 | * DRM operations: | ||
112 | */ | ||
113 | |||
114 | static int tilcdc_unload(struct drm_device *dev) | ||
115 | { | ||
116 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
117 | struct tilcdc_module *mod, *cur; | ||
118 | |||
119 | drm_kms_helper_poll_fini(dev); | ||
120 | drm_mode_config_cleanup(dev); | ||
121 | drm_vblank_cleanup(dev); | ||
122 | |||
123 | pm_runtime_get_sync(dev->dev); | ||
124 | drm_irq_uninstall(dev); | ||
125 | pm_runtime_put_sync(dev->dev); | ||
126 | |||
127 | #ifdef CONFIG_CPU_FREQ | ||
128 | cpufreq_unregister_notifier(&priv->freq_transition, | ||
129 | CPUFREQ_TRANSITION_NOTIFIER); | ||
130 | #endif | ||
131 | |||
132 | if (priv->clk) | ||
133 | clk_put(priv->clk); | ||
134 | |||
135 | if (priv->mmio) | ||
136 | iounmap(priv->mmio); | ||
137 | |||
138 | flush_workqueue(priv->wq); | ||
139 | destroy_workqueue(priv->wq); | ||
140 | |||
141 | dev->dev_private = NULL; | ||
142 | |||
143 | pm_runtime_disable(dev->dev); | ||
144 | |||
145 | list_for_each_entry_safe(mod, cur, &module_list, list) { | ||
146 | DBG("destroying module: %s", mod->name); | ||
147 | mod->funcs->destroy(mod); | ||
148 | } | ||
149 | |||
150 | kfree(priv); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int tilcdc_load(struct drm_device *dev, unsigned long flags) | ||
156 | { | ||
157 | struct platform_device *pdev = dev->platformdev; | ||
158 | struct device_node *node = pdev->dev.of_node; | ||
159 | struct tilcdc_drm_private *priv; | ||
160 | struct resource *res; | ||
161 | int ret; | ||
162 | |||
163 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
164 | if (!priv) { | ||
165 | dev_err(dev->dev, "failed to allocate private data\n"); | ||
166 | return -ENOMEM; | ||
167 | } | ||
168 | |||
169 | dev->dev_private = priv; | ||
170 | |||
171 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); | ||
172 | |||
173 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
174 | if (!res) { | ||
175 | dev_err(dev->dev, "failed to get memory resource\n"); | ||
176 | ret = -EINVAL; | ||
177 | goto fail; | ||
178 | } | ||
179 | |||
180 | priv->mmio = ioremap_nocache(res->start, resource_size(res)); | ||
181 | if (!priv->mmio) { | ||
182 | dev_err(dev->dev, "failed to ioremap\n"); | ||
183 | ret = -ENOMEM; | ||
184 | goto fail; | ||
185 | } | ||
186 | |||
187 | priv->clk = clk_get(dev->dev, "fck"); | ||
188 | if (IS_ERR(priv->clk)) { | ||
189 | dev_err(dev->dev, "failed to get functional clock\n"); | ||
190 | ret = -ENODEV; | ||
191 | goto fail; | ||
192 | } | ||
193 | |||
194 | priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); | ||
195 | if (IS_ERR(priv->clk)) { | ||
196 | dev_err(dev->dev, "failed to get display clock\n"); | ||
197 | ret = -ENODEV; | ||
198 | goto fail; | ||
199 | } | ||
200 | |||
201 | #ifdef CONFIG_CPU_FREQ | ||
202 | priv->lcd_fck_rate = clk_get_rate(priv->clk); | ||
203 | priv->freq_transition.notifier_call = cpufreq_transition; | ||
204 | ret = cpufreq_register_notifier(&priv->freq_transition, | ||
205 | CPUFREQ_TRANSITION_NOTIFIER); | ||
206 | if (ret) { | ||
207 | dev_err(dev->dev, "failed to register cpufreq notifier\n"); | ||
208 | goto fail; | ||
209 | } | ||
210 | #endif | ||
211 | |||
212 | if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth)) | ||
213 | priv->max_bandwidth = 1280 * 1024 * 60; | ||
214 | |||
215 | pm_runtime_enable(dev->dev); | ||
216 | |||
217 | /* Determine LCD IP Version */ | ||
218 | pm_runtime_get_sync(dev->dev); | ||
219 | switch (tilcdc_read(dev, LCDC_PID_REG)) { | ||
220 | case 0x4c100102: | ||
221 | priv->rev = 1; | ||
222 | break; | ||
223 | case 0x4f200800: | ||
224 | case 0x4f201000: | ||
225 | priv->rev = 2; | ||
226 | break; | ||
227 | default: | ||
228 | dev_warn(dev->dev, "Unknown PID Reg value 0x%08x, " | ||
229 | "defaulting to LCD revision 1\n", | ||
230 | tilcdc_read(dev, LCDC_PID_REG)); | ||
231 | priv->rev = 1; | ||
232 | break; | ||
233 | } | ||
234 | |||
235 | pm_runtime_put_sync(dev->dev); | ||
236 | |||
237 | ret = modeset_init(dev); | ||
238 | if (ret < 0) { | ||
239 | dev_err(dev->dev, "failed to initialize mode setting\n"); | ||
240 | goto fail; | ||
241 | } | ||
242 | |||
243 | ret = drm_vblank_init(dev, 1); | ||
244 | if (ret < 0) { | ||
245 | dev_err(dev->dev, "failed to initialize vblank\n"); | ||
246 | goto fail; | ||
247 | } | ||
248 | |||
249 | pm_runtime_get_sync(dev->dev); | ||
250 | ret = drm_irq_install(dev); | ||
251 | pm_runtime_put_sync(dev->dev); | ||
252 | if (ret < 0) { | ||
253 | dev_err(dev->dev, "failed to install IRQ handler\n"); | ||
254 | goto fail; | ||
255 | } | ||
256 | |||
257 | platform_set_drvdata(pdev, dev); | ||
258 | |||
259 | priv->fbdev = drm_fbdev_cma_init(dev, 16, | ||
260 | dev->mode_config.num_crtc, | ||
261 | dev->mode_config.num_connector); | ||
262 | |||
263 | drm_kms_helper_poll_init(dev); | ||
264 | |||
265 | return 0; | ||
266 | |||
267 | fail: | ||
268 | tilcdc_unload(dev); | ||
269 | return ret; | ||
270 | } | ||
271 | |||
272 | static void tilcdc_preclose(struct drm_device *dev, struct drm_file *file) | ||
273 | { | ||
274 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
275 | |||
276 | tilcdc_crtc_cancel_page_flip(priv->crtc, file); | ||
277 | } | ||
278 | |||
279 | static void tilcdc_lastclose(struct drm_device *dev) | ||
280 | { | ||
281 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
282 | drm_fbdev_cma_restore_mode(priv->fbdev); | ||
283 | } | ||
284 | |||
285 | static irqreturn_t tilcdc_irq(DRM_IRQ_ARGS) | ||
286 | { | ||
287 | struct drm_device *dev = arg; | ||
288 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
289 | return tilcdc_crtc_irq(priv->crtc); | ||
290 | } | ||
291 | |||
292 | static void tilcdc_irq_preinstall(struct drm_device *dev) | ||
293 | { | ||
294 | tilcdc_clear_irqstatus(dev, 0xffffffff); | ||
295 | } | ||
296 | |||
297 | static int tilcdc_irq_postinstall(struct drm_device *dev) | ||
298 | { | ||
299 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
300 | |||
301 | /* enable FIFO underflow irq: */ | ||
302 | if (priv->rev == 1) { | ||
303 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); | ||
304 | } else { | ||
305 | tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA); | ||
306 | } | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static void tilcdc_irq_uninstall(struct drm_device *dev) | ||
312 | { | ||
313 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
314 | |||
315 | /* disable irqs that we might have enabled: */ | ||
316 | if (priv->rev == 1) { | ||
317 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, | ||
318 | LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); | ||
319 | tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); | ||
320 | } else { | ||
321 | tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, | ||
322 | LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | | ||
323 | LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA | | ||
324 | LCDC_FRAME_DONE); | ||
325 | } | ||
326 | |||
327 | } | ||
328 | |||
329 | static void enable_vblank(struct drm_device *dev, bool enable) | ||
330 | { | ||
331 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
332 | u32 reg, mask; | ||
333 | |||
334 | if (priv->rev == 1) { | ||
335 | reg = LCDC_DMA_CTRL_REG; | ||
336 | mask = LCDC_V1_END_OF_FRAME_INT_ENA; | ||
337 | } else { | ||
338 | reg = LCDC_INT_ENABLE_SET_REG; | ||
339 | mask = LCDC_V2_END_OF_FRAME0_INT_ENA | | ||
340 | LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; | ||
341 | } | ||
342 | |||
343 | if (enable) | ||
344 | tilcdc_set(dev, reg, mask); | ||
345 | else | ||
346 | tilcdc_clear(dev, reg, mask); | ||
347 | } | ||
348 | |||
349 | static int tilcdc_enable_vblank(struct drm_device *dev, int crtc) | ||
350 | { | ||
351 | enable_vblank(dev, true); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static void tilcdc_disable_vblank(struct drm_device *dev, int crtc) | ||
356 | { | ||
357 | enable_vblank(dev, false); | ||
358 | } | ||
359 | |||
360 | #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) | ||
361 | static const struct { | ||
362 | const char *name; | ||
363 | uint8_t rev; | ||
364 | uint8_t save; | ||
365 | uint32_t reg; | ||
366 | } registers[] = { | ||
367 | #define REG(rev, save, reg) { #reg, rev, save, reg } | ||
368 | /* exists in revision 1: */ | ||
369 | REG(1, false, LCDC_PID_REG), | ||
370 | REG(1, true, LCDC_CTRL_REG), | ||
371 | REG(1, false, LCDC_STAT_REG), | ||
372 | REG(1, true, LCDC_RASTER_CTRL_REG), | ||
373 | REG(1, true, LCDC_RASTER_TIMING_0_REG), | ||
374 | REG(1, true, LCDC_RASTER_TIMING_1_REG), | ||
375 | REG(1, true, LCDC_RASTER_TIMING_2_REG), | ||
376 | REG(1, true, LCDC_DMA_CTRL_REG), | ||
377 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_0_REG), | ||
378 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_0_REG), | ||
379 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_1_REG), | ||
380 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_1_REG), | ||
381 | /* new in revision 2: */ | ||
382 | REG(2, false, LCDC_RAW_STAT_REG), | ||
383 | REG(2, false, LCDC_MASKED_STAT_REG), | ||
384 | REG(2, false, LCDC_INT_ENABLE_SET_REG), | ||
385 | REG(2, false, LCDC_INT_ENABLE_CLR_REG), | ||
386 | REG(2, false, LCDC_END_OF_INT_IND_REG), | ||
387 | REG(2, true, LCDC_CLK_ENABLE_REG), | ||
388 | REG(2, true, LCDC_INT_ENABLE_SET_REG), | ||
389 | #undef REG | ||
390 | }; | ||
391 | #endif | ||
392 | |||
393 | #ifdef CONFIG_DEBUG_FS | ||
394 | static int tilcdc_regs_show(struct seq_file *m, void *arg) | ||
395 | { | ||
396 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
397 | struct drm_device *dev = node->minor->dev; | ||
398 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
399 | unsigned i; | ||
400 | |||
401 | pm_runtime_get_sync(dev->dev); | ||
402 | |||
403 | seq_printf(m, "revision: %d\n", priv->rev); | ||
404 | |||
405 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
406 | if (priv->rev >= registers[i].rev) | ||
407 | seq_printf(m, "%s:\t %08x\n", registers[i].name, | ||
408 | tilcdc_read(dev, registers[i].reg)); | ||
409 | |||
410 | pm_runtime_put_sync(dev->dev); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | static int tilcdc_mm_show(struct seq_file *m, void *arg) | ||
416 | { | ||
417 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
418 | struct drm_device *dev = node->minor->dev; | ||
419 | return drm_mm_dump_table(m, dev->mm_private); | ||
420 | } | ||
421 | |||
422 | static struct drm_info_list tilcdc_debugfs_list[] = { | ||
423 | { "regs", tilcdc_regs_show, 0 }, | ||
424 | { "mm", tilcdc_mm_show, 0 }, | ||
425 | { "fb", drm_fb_cma_debugfs_show, 0 }, | ||
426 | }; | ||
427 | |||
428 | static int tilcdc_debugfs_init(struct drm_minor *minor) | ||
429 | { | ||
430 | struct drm_device *dev = minor->dev; | ||
431 | struct tilcdc_module *mod; | ||
432 | int ret; | ||
433 | |||
434 | ret = drm_debugfs_create_files(tilcdc_debugfs_list, | ||
435 | ARRAY_SIZE(tilcdc_debugfs_list), | ||
436 | minor->debugfs_root, minor); | ||
437 | |||
438 | list_for_each_entry(mod, &module_list, list) | ||
439 | if (mod->funcs->debugfs_init) | ||
440 | mod->funcs->debugfs_init(mod, minor); | ||
441 | |||
442 | if (ret) { | ||
443 | dev_err(dev->dev, "could not install tilcdc_debugfs_list\n"); | ||
444 | return ret; | ||
445 | } | ||
446 | |||
447 | return ret; | ||
448 | } | ||
449 | |||
450 | static void tilcdc_debugfs_cleanup(struct drm_minor *minor) | ||
451 | { | ||
452 | struct tilcdc_module *mod; | ||
453 | drm_debugfs_remove_files(tilcdc_debugfs_list, | ||
454 | ARRAY_SIZE(tilcdc_debugfs_list), minor); | ||
455 | |||
456 | list_for_each_entry(mod, &module_list, list) | ||
457 | if (mod->funcs->debugfs_cleanup) | ||
458 | mod->funcs->debugfs_cleanup(mod, minor); | ||
459 | } | ||
460 | #endif | ||
461 | |||
462 | static const struct file_operations fops = { | ||
463 | .owner = THIS_MODULE, | ||
464 | .open = drm_open, | ||
465 | .release = drm_release, | ||
466 | .unlocked_ioctl = drm_ioctl, | ||
467 | #ifdef CONFIG_COMPAT | ||
468 | .compat_ioctl = drm_compat_ioctl, | ||
469 | #endif | ||
470 | .poll = drm_poll, | ||
471 | .read = drm_read, | ||
472 | .fasync = drm_fasync, | ||
473 | .llseek = no_llseek, | ||
474 | .mmap = drm_gem_cma_mmap, | ||
475 | }; | ||
476 | |||
477 | static struct drm_driver tilcdc_driver = { | ||
478 | .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, | ||
479 | .load = tilcdc_load, | ||
480 | .unload = tilcdc_unload, | ||
481 | .preclose = tilcdc_preclose, | ||
482 | .lastclose = tilcdc_lastclose, | ||
483 | .irq_handler = tilcdc_irq, | ||
484 | .irq_preinstall = tilcdc_irq_preinstall, | ||
485 | .irq_postinstall = tilcdc_irq_postinstall, | ||
486 | .irq_uninstall = tilcdc_irq_uninstall, | ||
487 | .get_vblank_counter = drm_vblank_count, | ||
488 | .enable_vblank = tilcdc_enable_vblank, | ||
489 | .disable_vblank = tilcdc_disable_vblank, | ||
490 | .gem_free_object = drm_gem_cma_free_object, | ||
491 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
492 | .dumb_create = drm_gem_cma_dumb_create, | ||
493 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, | ||
494 | .dumb_destroy = drm_gem_cma_dumb_destroy, | ||
495 | #ifdef CONFIG_DEBUG_FS | ||
496 | .debugfs_init = tilcdc_debugfs_init, | ||
497 | .debugfs_cleanup = tilcdc_debugfs_cleanup, | ||
498 | #endif | ||
499 | .fops = &fops, | ||
500 | .name = "tilcdc", | ||
501 | .desc = "TI LCD Controller DRM", | ||
502 | .date = "20121205", | ||
503 | .major = 1, | ||
504 | .minor = 0, | ||
505 | }; | ||
506 | |||
507 | /* | ||
508 | * Power management: | ||
509 | */ | ||
510 | |||
511 | #ifdef CONFIG_PM_SLEEP | ||
512 | static int tilcdc_pm_suspend(struct device *dev) | ||
513 | { | ||
514 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
515 | struct tilcdc_drm_private *priv = ddev->dev_private; | ||
516 | unsigned i, n = 0; | ||
517 | |||
518 | drm_kms_helper_poll_disable(ddev); | ||
519 | |||
520 | /* Save register state: */ | ||
521 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
522 | if (registers[i].save && (priv->rev >= registers[i].rev)) | ||
523 | priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int tilcdc_pm_resume(struct device *dev) | ||
529 | { | ||
530 | struct drm_device *ddev = dev_get_drvdata(dev); | ||
531 | struct tilcdc_drm_private *priv = ddev->dev_private; | ||
532 | unsigned i, n = 0; | ||
533 | |||
534 | /* Restore register state: */ | ||
535 | for (i = 0; i < ARRAY_SIZE(registers); i++) | ||
536 | if (registers[i].save && (priv->rev >= registers[i].rev)) | ||
537 | tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]); | ||
538 | |||
539 | drm_kms_helper_poll_enable(ddev); | ||
540 | |||
541 | return 0; | ||
542 | } | ||
543 | #endif | ||
544 | |||
545 | static const struct dev_pm_ops tilcdc_pm_ops = { | ||
546 | SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume) | ||
547 | }; | ||
548 | |||
549 | /* | ||
550 | * Platform driver: | ||
551 | */ | ||
552 | |||
553 | static int tilcdc_pdev_probe(struct platform_device *pdev) | ||
554 | { | ||
555 | /* bail out early if no DT data: */ | ||
556 | if (!pdev->dev.of_node) { | ||
557 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
558 | return -ENXIO; | ||
559 | } | ||
560 | |||
561 | return drm_platform_init(&tilcdc_driver, pdev); | ||
562 | } | ||
563 | |||
564 | static int tilcdc_pdev_remove(struct platform_device *pdev) | ||
565 | { | ||
566 | drm_platform_exit(&tilcdc_driver, pdev); | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static struct of_device_id tilcdc_of_match[] = { | ||
572 | { .compatible = "ti,am33xx-tilcdc", }, | ||
573 | { }, | ||
574 | }; | ||
575 | MODULE_DEVICE_TABLE(of, tilcdc_of_match); | ||
576 | |||
577 | static struct platform_driver tilcdc_platform_driver = { | ||
578 | .probe = tilcdc_pdev_probe, | ||
579 | .remove = tilcdc_pdev_remove, | ||
580 | .driver = { | ||
581 | .owner = THIS_MODULE, | ||
582 | .name = "tilcdc", | ||
583 | .pm = &tilcdc_pm_ops, | ||
584 | .of_match_table = tilcdc_of_match, | ||
585 | }, | ||
586 | }; | ||
587 | |||
588 | static int __init tilcdc_drm_init(void) | ||
589 | { | ||
590 | DBG("init"); | ||
591 | tilcdc_tfp410_init(); | ||
592 | tilcdc_slave_init(); | ||
593 | tilcdc_panel_init(); | ||
594 | return platform_driver_register(&tilcdc_platform_driver); | ||
595 | } | ||
596 | |||
597 | static void __exit tilcdc_drm_fini(void) | ||
598 | { | ||
599 | DBG("fini"); | ||
600 | tilcdc_tfp410_fini(); | ||
601 | tilcdc_slave_fini(); | ||
602 | tilcdc_panel_fini(); | ||
603 | platform_driver_unregister(&tilcdc_platform_driver); | ||
604 | } | ||
605 | |||
606 | late_initcall(tilcdc_drm_init); | ||
607 | module_exit(tilcdc_drm_fini); | ||
608 | |||
609 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com"); | ||
610 | MODULE_DESCRIPTION("TI LCD Controller DRM Driver"); | ||
611 | 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_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c new file mode 100644 index 000000000000..580b74e2022b --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c | |||
@@ -0,0 +1,436 @@ | |||
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/pinctrl/pinmux.h> | ||
19 | #include <linux/pinctrl/consumer.h> | ||
20 | #include <linux/backlight.h> | ||
21 | #include <video/display_timing.h> | ||
22 | #include <video/of_display_timing.h> | ||
23 | #include <video/videomode.h> | ||
24 | |||
25 | #include "tilcdc_drv.h" | ||
26 | |||
27 | struct panel_module { | ||
28 | struct tilcdc_module base; | ||
29 | struct tilcdc_panel_info *info; | ||
30 | struct display_timings *timings; | ||
31 | struct backlight_device *backlight; | ||
32 | }; | ||
33 | #define to_panel_module(x) container_of(x, struct panel_module, base) | ||
34 | |||
35 | |||
36 | /* | ||
37 | * Encoder: | ||
38 | */ | ||
39 | |||
40 | struct panel_encoder { | ||
41 | struct drm_encoder base; | ||
42 | struct panel_module *mod; | ||
43 | }; | ||
44 | #define to_panel_encoder(x) container_of(x, struct panel_encoder, base) | ||
45 | |||
46 | |||
47 | static void panel_encoder_destroy(struct drm_encoder *encoder) | ||
48 | { | ||
49 | struct panel_encoder *panel_encoder = to_panel_encoder(encoder); | ||
50 | drm_encoder_cleanup(encoder); | ||
51 | kfree(panel_encoder); | ||
52 | } | ||
53 | |||
54 | static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
55 | { | ||
56 | struct panel_encoder *panel_encoder = to_panel_encoder(encoder); | ||
57 | struct backlight_device *backlight = panel_encoder->mod->backlight; | ||
58 | |||
59 | if (!backlight) | ||
60 | return; | ||
61 | |||
62 | backlight->props.power = mode == DRM_MODE_DPMS_ON | ||
63 | ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||
64 | backlight_update_status(backlight); | ||
65 | } | ||
66 | |||
67 | static bool panel_encoder_mode_fixup(struct drm_encoder *encoder, | ||
68 | const struct drm_display_mode *mode, | ||
69 | struct drm_display_mode *adjusted_mode) | ||
70 | { | ||
71 | /* nothing needed */ | ||
72 | return true; | ||
73 | } | ||
74 | |||
75 | static void panel_encoder_prepare(struct drm_encoder *encoder) | ||
76 | { | ||
77 | struct panel_encoder *panel_encoder = to_panel_encoder(encoder); | ||
78 | panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); | ||
79 | tilcdc_crtc_set_panel_info(encoder->crtc, panel_encoder->mod->info); | ||
80 | } | ||
81 | |||
82 | static void panel_encoder_commit(struct drm_encoder *encoder) | ||
83 | { | ||
84 | panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON); | ||
85 | } | ||
86 | |||
87 | static void panel_encoder_mode_set(struct drm_encoder *encoder, | ||
88 | struct drm_display_mode *mode, | ||
89 | struct drm_display_mode *adjusted_mode) | ||
90 | { | ||
91 | /* nothing needed */ | ||
92 | } | ||
93 | |||
94 | static const struct drm_encoder_funcs panel_encoder_funcs = { | ||
95 | .destroy = panel_encoder_destroy, | ||
96 | }; | ||
97 | |||
98 | static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = { | ||
99 | .dpms = panel_encoder_dpms, | ||
100 | .mode_fixup = panel_encoder_mode_fixup, | ||
101 | .prepare = panel_encoder_prepare, | ||
102 | .commit = panel_encoder_commit, | ||
103 | .mode_set = panel_encoder_mode_set, | ||
104 | }; | ||
105 | |||
106 | static struct drm_encoder *panel_encoder_create(struct drm_device *dev, | ||
107 | struct panel_module *mod) | ||
108 | { | ||
109 | struct panel_encoder *panel_encoder; | ||
110 | struct drm_encoder *encoder; | ||
111 | int ret; | ||
112 | |||
113 | panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL); | ||
114 | if (!panel_encoder) { | ||
115 | dev_err(dev->dev, "allocation failed\n"); | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | panel_encoder->mod = mod; | ||
120 | |||
121 | encoder = &panel_encoder->base; | ||
122 | encoder->possible_crtcs = 1; | ||
123 | |||
124 | ret = drm_encoder_init(dev, encoder, &panel_encoder_funcs, | ||
125 | DRM_MODE_ENCODER_LVDS); | ||
126 | if (ret < 0) | ||
127 | goto fail; | ||
128 | |||
129 | drm_encoder_helper_add(encoder, &panel_encoder_helper_funcs); | ||
130 | |||
131 | return encoder; | ||
132 | |||
133 | fail: | ||
134 | panel_encoder_destroy(encoder); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Connector: | ||
140 | */ | ||
141 | |||
142 | struct panel_connector { | ||
143 | struct drm_connector base; | ||
144 | |||
145 | struct drm_encoder *encoder; /* our connected encoder */ | ||
146 | struct panel_module *mod; | ||
147 | }; | ||
148 | #define to_panel_connector(x) container_of(x, struct panel_connector, base) | ||
149 | |||
150 | |||
151 | static void panel_connector_destroy(struct drm_connector *connector) | ||
152 | { | ||
153 | struct panel_connector *panel_connector = to_panel_connector(connector); | ||
154 | drm_connector_cleanup(connector); | ||
155 | kfree(panel_connector); | ||
156 | } | ||
157 | |||
158 | static enum drm_connector_status panel_connector_detect( | ||
159 | struct drm_connector *connector, | ||
160 | bool force) | ||
161 | { | ||
162 | return connector_status_connected; | ||
163 | } | ||
164 | |||
165 | static int panel_connector_get_modes(struct drm_connector *connector) | ||
166 | { | ||
167 | struct drm_device *dev = connector->dev; | ||
168 | struct panel_connector *panel_connector = to_panel_connector(connector); | ||
169 | struct display_timings *timings = panel_connector->mod->timings; | ||
170 | int i; | ||
171 | |||
172 | for (i = 0; i < timings->num_timings; i++) { | ||
173 | struct drm_display_mode *mode = drm_mode_create(dev); | ||
174 | struct videomode vm; | ||
175 | |||
176 | if (videomode_from_timing(timings, &vm, i)) | ||
177 | break; | ||
178 | |||
179 | drm_display_mode_from_videomode(&vm, mode); | ||
180 | |||
181 | mode->type = DRM_MODE_TYPE_DRIVER; | ||
182 | |||
183 | if (timings->native_mode == i) | ||
184 | mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
185 | |||
186 | drm_mode_set_name(mode); | ||
187 | drm_mode_probed_add(connector, mode); | ||
188 | } | ||
189 | |||
190 | return i; | ||
191 | } | ||
192 | |||
193 | static int panel_connector_mode_valid(struct drm_connector *connector, | ||
194 | struct drm_display_mode *mode) | ||
195 | { | ||
196 | struct tilcdc_drm_private *priv = connector->dev->dev_private; | ||
197 | /* our only constraints are what the crtc can generate: */ | ||
198 | return tilcdc_crtc_mode_valid(priv->crtc, mode); | ||
199 | } | ||
200 | |||
201 | static struct drm_encoder *panel_connector_best_encoder( | ||
202 | struct drm_connector *connector) | ||
203 | { | ||
204 | struct panel_connector *panel_connector = to_panel_connector(connector); | ||
205 | return panel_connector->encoder; | ||
206 | } | ||
207 | |||
208 | static const struct drm_connector_funcs panel_connector_funcs = { | ||
209 | .destroy = panel_connector_destroy, | ||
210 | .dpms = drm_helper_connector_dpms, | ||
211 | .detect = panel_connector_detect, | ||
212 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
213 | }; | ||
214 | |||
215 | static const struct drm_connector_helper_funcs panel_connector_helper_funcs = { | ||
216 | .get_modes = panel_connector_get_modes, | ||
217 | .mode_valid = panel_connector_mode_valid, | ||
218 | .best_encoder = panel_connector_best_encoder, | ||
219 | }; | ||
220 | |||
221 | static struct drm_connector *panel_connector_create(struct drm_device *dev, | ||
222 | struct panel_module *mod, struct drm_encoder *encoder) | ||
223 | { | ||
224 | struct panel_connector *panel_connector; | ||
225 | struct drm_connector *connector; | ||
226 | int ret; | ||
227 | |||
228 | panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL); | ||
229 | if (!panel_connector) { | ||
230 | dev_err(dev->dev, "allocation failed\n"); | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
234 | panel_connector->encoder = encoder; | ||
235 | panel_connector->mod = mod; | ||
236 | |||
237 | connector = &panel_connector->base; | ||
238 | |||
239 | drm_connector_init(dev, connector, &panel_connector_funcs, | ||
240 | DRM_MODE_CONNECTOR_LVDS); | ||
241 | drm_connector_helper_add(connector, &panel_connector_helper_funcs); | ||
242 | |||
243 | connector->interlace_allowed = 0; | ||
244 | connector->doublescan_allowed = 0; | ||
245 | |||
246 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
247 | if (ret) | ||
248 | goto fail; | ||
249 | |||
250 | drm_sysfs_connector_add(connector); | ||
251 | |||
252 | return connector; | ||
253 | |||
254 | fail: | ||
255 | panel_connector_destroy(connector); | ||
256 | return NULL; | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * Module: | ||
261 | */ | ||
262 | |||
263 | static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) | ||
264 | { | ||
265 | struct panel_module *panel_mod = to_panel_module(mod); | ||
266 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
267 | struct drm_encoder *encoder; | ||
268 | struct drm_connector *connector; | ||
269 | |||
270 | encoder = panel_encoder_create(dev, panel_mod); | ||
271 | if (!encoder) | ||
272 | return -ENOMEM; | ||
273 | |||
274 | connector = panel_connector_create(dev, panel_mod, encoder); | ||
275 | if (!connector) | ||
276 | return -ENOMEM; | ||
277 | |||
278 | priv->encoders[priv->num_encoders++] = encoder; | ||
279 | priv->connectors[priv->num_connectors++] = connector; | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void panel_destroy(struct tilcdc_module *mod) | ||
285 | { | ||
286 | struct panel_module *panel_mod = to_panel_module(mod); | ||
287 | |||
288 | if (panel_mod->timings) { | ||
289 | display_timings_release(panel_mod->timings); | ||
290 | kfree(panel_mod->timings); | ||
291 | } | ||
292 | |||
293 | tilcdc_module_cleanup(mod); | ||
294 | kfree(panel_mod->info); | ||
295 | kfree(panel_mod); | ||
296 | } | ||
297 | |||
298 | static const struct tilcdc_module_ops panel_module_ops = { | ||
299 | .modeset_init = panel_modeset_init, | ||
300 | .destroy = panel_destroy, | ||
301 | }; | ||
302 | |||
303 | /* | ||
304 | * Device: | ||
305 | */ | ||
306 | |||
307 | /* maybe move this somewhere common if it is needed by other outputs? */ | ||
308 | static struct tilcdc_panel_info * of_get_panel_info(struct device_node *np) | ||
309 | { | ||
310 | struct device_node *info_np; | ||
311 | struct tilcdc_panel_info *info; | ||
312 | int ret = 0; | ||
313 | |||
314 | if (!np) { | ||
315 | pr_err("%s: no devicenode given\n", __func__); | ||
316 | return NULL; | ||
317 | } | ||
318 | |||
319 | info_np = of_get_child_by_name(np, "panel-info"); | ||
320 | if (!info_np) { | ||
321 | pr_err("%s: could not find panel-info node\n", __func__); | ||
322 | return NULL; | ||
323 | } | ||
324 | |||
325 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
326 | if (!info) { | ||
327 | pr_err("%s: allocation failed\n", __func__); | ||
328 | return NULL; | ||
329 | } | ||
330 | |||
331 | ret |= of_property_read_u32(info_np, "ac-bias", &info->ac_bias); | ||
332 | ret |= of_property_read_u32(info_np, "ac-bias-intrpt", &info->ac_bias_intrpt); | ||
333 | ret |= of_property_read_u32(info_np, "dma-burst-sz", &info->dma_burst_sz); | ||
334 | ret |= of_property_read_u32(info_np, "bpp", &info->bpp); | ||
335 | ret |= of_property_read_u32(info_np, "fdd", &info->fdd); | ||
336 | ret |= of_property_read_u32(info_np, "sync-edge", &info->sync_edge); | ||
337 | ret |= of_property_read_u32(info_np, "sync-ctrl", &info->sync_ctrl); | ||
338 | ret |= of_property_read_u32(info_np, "raster-order", &info->raster_order); | ||
339 | ret |= of_property_read_u32(info_np, "fifo-th", &info->fifo_th); | ||
340 | |||
341 | /* optional: */ | ||
342 | info->tft_alt_mode = of_property_read_bool(info_np, "tft-alt-mode"); | ||
343 | info->invert_pxl_clk = of_property_read_bool(info_np, "invert-pxl-clk"); | ||
344 | |||
345 | if (ret) { | ||
346 | pr_err("%s: error reading panel-info properties\n", __func__); | ||
347 | kfree(info); | ||
348 | return NULL; | ||
349 | } | ||
350 | |||
351 | return info; | ||
352 | } | ||
353 | |||
354 | static struct of_device_id panel_of_match[]; | ||
355 | |||
356 | static int panel_probe(struct platform_device *pdev) | ||
357 | { | ||
358 | struct device_node *node = pdev->dev.of_node; | ||
359 | struct panel_module *panel_mod; | ||
360 | struct tilcdc_module *mod; | ||
361 | struct pinctrl *pinctrl; | ||
362 | int ret = -EINVAL; | ||
363 | |||
364 | |||
365 | /* bail out early if no DT data: */ | ||
366 | if (!node) { | ||
367 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
368 | return -ENXIO; | ||
369 | } | ||
370 | |||
371 | panel_mod = kzalloc(sizeof(*panel_mod), GFP_KERNEL); | ||
372 | if (!panel_mod) | ||
373 | return -ENOMEM; | ||
374 | |||
375 | mod = &panel_mod->base; | ||
376 | |||
377 | tilcdc_module_init(mod, "panel", &panel_module_ops); | ||
378 | |||
379 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
380 | if (IS_ERR(pinctrl)) | ||
381 | dev_warn(&pdev->dev, "pins are not configured\n"); | ||
382 | |||
383 | |||
384 | panel_mod->timings = of_get_display_timings(node); | ||
385 | if (!panel_mod->timings) { | ||
386 | dev_err(&pdev->dev, "could not get panel timings\n"); | ||
387 | goto fail; | ||
388 | } | ||
389 | |||
390 | panel_mod->info = of_get_panel_info(node); | ||
391 | if (!panel_mod->info) { | ||
392 | dev_err(&pdev->dev, "could not get panel info\n"); | ||
393 | goto fail; | ||
394 | } | ||
395 | |||
396 | panel_mod->backlight = of_find_backlight_by_node(node); | ||
397 | if (panel_mod->backlight) | ||
398 | dev_info(&pdev->dev, "found backlight\n"); | ||
399 | |||
400 | return 0; | ||
401 | |||
402 | fail: | ||
403 | panel_destroy(mod); | ||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | static int panel_remove(struct platform_device *pdev) | ||
408 | { | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static struct of_device_id panel_of_match[] = { | ||
413 | { .compatible = "ti,tilcdc,panel", }, | ||
414 | { }, | ||
415 | }; | ||
416 | MODULE_DEVICE_TABLE(of, panel_of_match); | ||
417 | |||
418 | struct platform_driver panel_driver = { | ||
419 | .probe = panel_probe, | ||
420 | .remove = panel_remove, | ||
421 | .driver = { | ||
422 | .owner = THIS_MODULE, | ||
423 | .name = "panel", | ||
424 | .of_match_table = panel_of_match, | ||
425 | }, | ||
426 | }; | ||
427 | |||
428 | int __init tilcdc_panel_init(void) | ||
429 | { | ||
430 | return platform_driver_register(&panel_driver); | ||
431 | } | ||
432 | |||
433 | void __exit tilcdc_panel_fini(void) | ||
434 | { | ||
435 | platform_driver_unregister(&panel_driver); | ||
436 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.h b/drivers/gpu/drm/tilcdc/tilcdc_panel.h new file mode 100644 index 000000000000..7db40aacc74a --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.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_PANEL_H__ | ||
19 | #define __TILCDC_PANEL_H__ | ||
20 | |||
21 | /* sub-module for generic lcd panel output */ | ||
22 | |||
23 | int tilcdc_panel_init(void); | ||
24 | void tilcdc_panel_fini(void); | ||
25 | |||
26 | #endif /* __TILCDC_PANEL_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_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c new file mode 100644 index 000000000000..568dc1c08e6c --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c | |||
@@ -0,0 +1,376 @@ | |||
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/pinctrl/pinmux.h> | ||
21 | #include <linux/pinctrl/consumer.h> | ||
22 | #include <drm/drm_encoder_slave.h> | ||
23 | |||
24 | #include "tilcdc_drv.h" | ||
25 | |||
26 | struct slave_module { | ||
27 | struct tilcdc_module base; | ||
28 | struct i2c_adapter *i2c; | ||
29 | }; | ||
30 | #define to_slave_module(x) container_of(x, struct slave_module, base) | ||
31 | |||
32 | static const struct tilcdc_panel_info slave_info = { | ||
33 | .bpp = 16, | ||
34 | .ac_bias = 255, | ||
35 | .ac_bias_intrpt = 0, | ||
36 | .dma_burst_sz = 16, | ||
37 | .fdd = 0x80, | ||
38 | .tft_alt_mode = 0, | ||
39 | .sync_edge = 0, | ||
40 | .sync_ctrl = 1, | ||
41 | .raster_order = 0, | ||
42 | }; | ||
43 | |||
44 | |||
45 | /* | ||
46 | * Encoder: | ||
47 | */ | ||
48 | |||
49 | struct slave_encoder { | ||
50 | struct drm_encoder_slave base; | ||
51 | struct slave_module *mod; | ||
52 | }; | ||
53 | #define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base) | ||
54 | |||
55 | static inline struct drm_encoder_slave_funcs * | ||
56 | get_slave_funcs(struct drm_encoder *enc) | ||
57 | { | ||
58 | return to_encoder_slave(enc)->slave_funcs; | ||
59 | } | ||
60 | |||
61 | static void slave_encoder_destroy(struct drm_encoder *encoder) | ||
62 | { | ||
63 | struct slave_encoder *slave_encoder = to_slave_encoder(encoder); | ||
64 | if (get_slave_funcs(encoder)) | ||
65 | get_slave_funcs(encoder)->destroy(encoder); | ||
66 | drm_encoder_cleanup(encoder); | ||
67 | kfree(slave_encoder); | ||
68 | } | ||
69 | |||
70 | static void slave_encoder_prepare(struct drm_encoder *encoder) | ||
71 | { | ||
72 | drm_i2c_encoder_prepare(encoder); | ||
73 | tilcdc_crtc_set_panel_info(encoder->crtc, &slave_info); | ||
74 | } | ||
75 | |||
76 | static const struct drm_encoder_funcs slave_encoder_funcs = { | ||
77 | .destroy = slave_encoder_destroy, | ||
78 | }; | ||
79 | |||
80 | static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = { | ||
81 | .dpms = drm_i2c_encoder_dpms, | ||
82 | .mode_fixup = drm_i2c_encoder_mode_fixup, | ||
83 | .prepare = slave_encoder_prepare, | ||
84 | .commit = drm_i2c_encoder_commit, | ||
85 | .mode_set = drm_i2c_encoder_mode_set, | ||
86 | .save = drm_i2c_encoder_save, | ||
87 | .restore = drm_i2c_encoder_restore, | ||
88 | }; | ||
89 | |||
90 | static const struct i2c_board_info info = { | ||
91 | I2C_BOARD_INFO("tda998x", 0x70) | ||
92 | }; | ||
93 | |||
94 | static struct drm_encoder *slave_encoder_create(struct drm_device *dev, | ||
95 | struct slave_module *mod) | ||
96 | { | ||
97 | struct slave_encoder *slave_encoder; | ||
98 | struct drm_encoder *encoder; | ||
99 | int ret; | ||
100 | |||
101 | slave_encoder = kzalloc(sizeof(*slave_encoder), GFP_KERNEL); | ||
102 | if (!slave_encoder) { | ||
103 | dev_err(dev->dev, "allocation failed\n"); | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | slave_encoder->mod = mod; | ||
108 | |||
109 | encoder = &slave_encoder->base.base; | ||
110 | encoder->possible_crtcs = 1; | ||
111 | |||
112 | ret = drm_encoder_init(dev, encoder, &slave_encoder_funcs, | ||
113 | DRM_MODE_ENCODER_TMDS); | ||
114 | if (ret) | ||
115 | goto fail; | ||
116 | |||
117 | drm_encoder_helper_add(encoder, &slave_encoder_helper_funcs); | ||
118 | |||
119 | ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), mod->i2c, &info); | ||
120 | if (ret) | ||
121 | goto fail; | ||
122 | |||
123 | return encoder; | ||
124 | |||
125 | fail: | ||
126 | slave_encoder_destroy(encoder); | ||
127 | return NULL; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Connector: | ||
132 | */ | ||
133 | |||
134 | struct slave_connector { | ||
135 | struct drm_connector base; | ||
136 | |||
137 | struct drm_encoder *encoder; /* our connected encoder */ | ||
138 | struct slave_module *mod; | ||
139 | }; | ||
140 | #define to_slave_connector(x) container_of(x, struct slave_connector, base) | ||
141 | |||
142 | static void slave_connector_destroy(struct drm_connector *connector) | ||
143 | { | ||
144 | struct slave_connector *slave_connector = to_slave_connector(connector); | ||
145 | drm_connector_cleanup(connector); | ||
146 | kfree(slave_connector); | ||
147 | } | ||
148 | |||
149 | static enum drm_connector_status slave_connector_detect( | ||
150 | struct drm_connector *connector, | ||
151 | bool force) | ||
152 | { | ||
153 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
154 | return get_slave_funcs(encoder)->detect(encoder, connector); | ||
155 | } | ||
156 | |||
157 | static int slave_connector_get_modes(struct drm_connector *connector) | ||
158 | { | ||
159 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
160 | return get_slave_funcs(encoder)->get_modes(encoder, connector); | ||
161 | } | ||
162 | |||
163 | static int slave_connector_mode_valid(struct drm_connector *connector, | ||
164 | struct drm_display_mode *mode) | ||
165 | { | ||
166 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
167 | struct tilcdc_drm_private *priv = connector->dev->dev_private; | ||
168 | int ret; | ||
169 | |||
170 | ret = tilcdc_crtc_mode_valid(priv->crtc, mode); | ||
171 | if (ret != MODE_OK) | ||
172 | return ret; | ||
173 | |||
174 | return get_slave_funcs(encoder)->mode_valid(encoder, mode); | ||
175 | } | ||
176 | |||
177 | static struct drm_encoder *slave_connector_best_encoder( | ||
178 | struct drm_connector *connector) | ||
179 | { | ||
180 | struct slave_connector *slave_connector = to_slave_connector(connector); | ||
181 | return slave_connector->encoder; | ||
182 | } | ||
183 | |||
184 | static int slave_connector_set_property(struct drm_connector *connector, | ||
185 | struct drm_property *property, uint64_t value) | ||
186 | { | ||
187 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
188 | return get_slave_funcs(encoder)->set_property(encoder, | ||
189 | connector, property, value); | ||
190 | } | ||
191 | |||
192 | static const struct drm_connector_funcs slave_connector_funcs = { | ||
193 | .destroy = slave_connector_destroy, | ||
194 | .dpms = drm_helper_connector_dpms, | ||
195 | .detect = slave_connector_detect, | ||
196 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
197 | .set_property = slave_connector_set_property, | ||
198 | }; | ||
199 | |||
200 | static const struct drm_connector_helper_funcs slave_connector_helper_funcs = { | ||
201 | .get_modes = slave_connector_get_modes, | ||
202 | .mode_valid = slave_connector_mode_valid, | ||
203 | .best_encoder = slave_connector_best_encoder, | ||
204 | }; | ||
205 | |||
206 | static struct drm_connector *slave_connector_create(struct drm_device *dev, | ||
207 | struct slave_module *mod, struct drm_encoder *encoder) | ||
208 | { | ||
209 | struct slave_connector *slave_connector; | ||
210 | struct drm_connector *connector; | ||
211 | int ret; | ||
212 | |||
213 | slave_connector = kzalloc(sizeof(*slave_connector), GFP_KERNEL); | ||
214 | if (!slave_connector) { | ||
215 | dev_err(dev->dev, "allocation failed\n"); | ||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | slave_connector->encoder = encoder; | ||
220 | slave_connector->mod = mod; | ||
221 | |||
222 | connector = &slave_connector->base; | ||
223 | |||
224 | drm_connector_init(dev, connector, &slave_connector_funcs, | ||
225 | DRM_MODE_CONNECTOR_HDMIA); | ||
226 | drm_connector_helper_add(connector, &slave_connector_helper_funcs); | ||
227 | |||
228 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | | ||
229 | DRM_CONNECTOR_POLL_DISCONNECT; | ||
230 | |||
231 | connector->interlace_allowed = 0; | ||
232 | connector->doublescan_allowed = 0; | ||
233 | |||
234 | get_slave_funcs(encoder)->create_resources(encoder, connector); | ||
235 | |||
236 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
237 | if (ret) | ||
238 | goto fail; | ||
239 | |||
240 | drm_sysfs_connector_add(connector); | ||
241 | |||
242 | return connector; | ||
243 | |||
244 | fail: | ||
245 | slave_connector_destroy(connector); | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Module: | ||
251 | */ | ||
252 | |||
253 | static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) | ||
254 | { | ||
255 | struct slave_module *slave_mod = to_slave_module(mod); | ||
256 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
257 | struct drm_encoder *encoder; | ||
258 | struct drm_connector *connector; | ||
259 | |||
260 | encoder = slave_encoder_create(dev, slave_mod); | ||
261 | if (!encoder) | ||
262 | return -ENOMEM; | ||
263 | |||
264 | connector = slave_connector_create(dev, slave_mod, encoder); | ||
265 | if (!connector) | ||
266 | return -ENOMEM; | ||
267 | |||
268 | priv->encoders[priv->num_encoders++] = encoder; | ||
269 | priv->connectors[priv->num_connectors++] = connector; | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static void slave_destroy(struct tilcdc_module *mod) | ||
275 | { | ||
276 | struct slave_module *slave_mod = to_slave_module(mod); | ||
277 | |||
278 | tilcdc_module_cleanup(mod); | ||
279 | kfree(slave_mod); | ||
280 | } | ||
281 | |||
282 | static const struct tilcdc_module_ops slave_module_ops = { | ||
283 | .modeset_init = slave_modeset_init, | ||
284 | .destroy = slave_destroy, | ||
285 | }; | ||
286 | |||
287 | /* | ||
288 | * Device: | ||
289 | */ | ||
290 | |||
291 | static struct of_device_id slave_of_match[]; | ||
292 | |||
293 | static int slave_probe(struct platform_device *pdev) | ||
294 | { | ||
295 | struct device_node *node = pdev->dev.of_node; | ||
296 | struct device_node *i2c_node; | ||
297 | struct slave_module *slave_mod; | ||
298 | struct tilcdc_module *mod; | ||
299 | struct pinctrl *pinctrl; | ||
300 | uint32_t i2c_phandle; | ||
301 | int ret = -EINVAL; | ||
302 | |||
303 | /* bail out early if no DT data: */ | ||
304 | if (!node) { | ||
305 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
306 | return -ENXIO; | ||
307 | } | ||
308 | |||
309 | slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL); | ||
310 | if (!slave_mod) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | mod = &slave_mod->base; | ||
314 | |||
315 | tilcdc_module_init(mod, "slave", &slave_module_ops); | ||
316 | |||
317 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
318 | if (IS_ERR(pinctrl)) | ||
319 | dev_warn(&pdev->dev, "pins are not configured\n"); | ||
320 | |||
321 | if (of_property_read_u32(node, "i2c", &i2c_phandle)) { | ||
322 | dev_err(&pdev->dev, "could not get i2c bus phandle\n"); | ||
323 | goto fail; | ||
324 | } | ||
325 | |||
326 | i2c_node = of_find_node_by_phandle(i2c_phandle); | ||
327 | if (!i2c_node) { | ||
328 | dev_err(&pdev->dev, "could not get i2c bus node\n"); | ||
329 | goto fail; | ||
330 | } | ||
331 | |||
332 | slave_mod->i2c = of_find_i2c_adapter_by_node(i2c_node); | ||
333 | if (!slave_mod->i2c) { | ||
334 | dev_err(&pdev->dev, "could not get i2c\n"); | ||
335 | goto fail; | ||
336 | } | ||
337 | |||
338 | of_node_put(i2c_node); | ||
339 | |||
340 | return 0; | ||
341 | |||
342 | fail: | ||
343 | slave_destroy(mod); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static int slave_remove(struct platform_device *pdev) | ||
348 | { | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static struct of_device_id slave_of_match[] = { | ||
353 | { .compatible = "ti,tilcdc,slave", }, | ||
354 | { }, | ||
355 | }; | ||
356 | MODULE_DEVICE_TABLE(of, slave_of_match); | ||
357 | |||
358 | struct platform_driver slave_driver = { | ||
359 | .probe = slave_probe, | ||
360 | .remove = slave_remove, | ||
361 | .driver = { | ||
362 | .owner = THIS_MODULE, | ||
363 | .name = "slave", | ||
364 | .of_match_table = slave_of_match, | ||
365 | }, | ||
366 | }; | ||
367 | |||
368 | int __init tilcdc_slave_init(void) | ||
369 | { | ||
370 | return platform_driver_register(&slave_driver); | ||
371 | } | ||
372 | |||
373 | void __exit tilcdc_slave_fini(void) | ||
374 | { | ||
375 | platform_driver_unregister(&slave_driver); | ||
376 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.h b/drivers/gpu/drm/tilcdc/tilcdc_slave.h new file mode 100644 index 000000000000..2f8504848320 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.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_SLAVE_H__ | ||
19 | #define __TILCDC_SLAVE_H__ | ||
20 | |||
21 | /* sub-module for i2c slave encoder output */ | ||
22 | |||
23 | int tilcdc_slave_init(void); | ||
24 | void tilcdc_slave_fini(void); | ||
25 | |||
26 | #endif /* __TILCDC_SLAVE_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__ */ | ||
diff --git a/include/drm/drm_encoder_slave.h b/include/drm/drm_encoder_slave.h index b0c11a7809bb..8b9cc3671858 100644 --- a/include/drm/drm_encoder_slave.h +++ b/include/drm/drm_encoder_slave.h | |||
@@ -159,4 +159,24 @@ static inline void drm_i2c_encoder_unregister(struct drm_i2c_encoder_driver *dri | |||
159 | 159 | ||
160 | void drm_i2c_encoder_destroy(struct drm_encoder *encoder); | 160 | void drm_i2c_encoder_destroy(struct drm_encoder *encoder); |
161 | 161 | ||
162 | |||
163 | /* | ||
164 | * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: | ||
165 | */ | ||
166 | |||
167 | void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode); | ||
168 | bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, | ||
169 | const struct drm_display_mode *mode, | ||
170 | struct drm_display_mode *adjusted_mode); | ||
171 | void drm_i2c_encoder_prepare(struct drm_encoder *encoder); | ||
172 | void drm_i2c_encoder_commit(struct drm_encoder *encoder); | ||
173 | void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, | ||
174 | struct drm_display_mode *mode, | ||
175 | struct drm_display_mode *adjusted_mode); | ||
176 | enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, | ||
177 | struct drm_connector *connector); | ||
178 | void drm_i2c_encoder_save(struct drm_encoder *encoder); | ||
179 | void drm_i2c_encoder_restore(struct drm_encoder *encoder); | ||
180 | |||
181 | |||
162 | #endif | 182 | #endif |
diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 76c709837543..4a3fc244301c 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h | |||
@@ -23,5 +23,10 @@ struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, | |||
23 | struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, | 23 | struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, |
24 | unsigned int plane); | 24 | unsigned int plane); |
25 | 25 | ||
26 | #ifdef CONFIG_DEBUG_FS | ||
27 | void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m); | ||
28 | int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg); | ||
29 | #endif | ||
30 | |||
26 | #endif | 31 | #endif |
27 | 32 | ||
diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index f0f6b1af25ad..63397ced9254 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h | |||
@@ -41,4 +41,8 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, | |||
41 | 41 | ||
42 | extern const struct vm_operations_struct drm_gem_cma_vm_ops; | 42 | extern const struct vm_operations_struct drm_gem_cma_vm_ops; |
43 | 43 | ||
44 | #ifdef CONFIG_DEBUG_FS | ||
45 | void drm_gem_cma_describe(struct drm_gem_cma_object *obj, struct seq_file *m); | ||
46 | #endif | ||
47 | |||
44 | #endif /* __DRM_GEM_CMA_HELPER_H__ */ | 48 | #endif /* __DRM_GEM_CMA_HELPER_H__ */ |