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