| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 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 |  | 
| Sean Paul | ce2f2c3 | 2016-09-21 06:14:53 -0700 | [diff] [blame] | 18 | #include <drm/drm_atomic.h> | 
| Jyri Sarha | 305198d | 2016-04-07 15:05:16 +0300 | [diff] [blame] | 19 | #include <drm/drm_atomic_helper.h> | 
| Sean Paul | ce2f2c3 | 2016-09-21 06:14:53 -0700 | [diff] [blame] | 20 | #include <drm/drm_crtc.h> | 
|  | 21 | #include <drm/drm_flip_work.h> | 
|  | 22 | #include <drm/drm_plane_helper.h> | 
| Jyri Sarha | 4e910c7 | 2016-09-06 22:55:33 +0300 | [diff] [blame] | 23 | #include <linux/workqueue.h> | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 24 |  | 
|  | 25 | #include "tilcdc_drv.h" | 
|  | 26 | #include "tilcdc_regs.h" | 
|  | 27 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 28 | #define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 | 
|  | 29 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 30 | struct tilcdc_crtc { | 
|  | 31 | struct drm_crtc base; | 
|  | 32 |  | 
| Jyri Sarha | 47f571c | 2016-04-07 15:04:18 +0300 | [diff] [blame] | 33 | struct drm_plane primary; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 34 | const struct tilcdc_panel_info *info; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 35 | struct drm_pending_vblank_event *event; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 36 | struct mutex enable_lock; | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 37 | bool enabled; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 38 | bool shutdown; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 39 | wait_queue_head_t frame_done_wq; | 
|  | 40 | bool frame_done; | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 41 | spinlock_t irq_lock; | 
|  | 42 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 43 | unsigned int lcd_fck_rate; | 
|  | 44 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 45 | ktime_t last_vblank; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 46 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 47 | struct drm_framebuffer *curr_fb; | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 48 | struct drm_framebuffer *next_fb; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 49 |  | 
|  | 50 | /* for deferred fb unref's: */ | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 51 | struct drm_flip_work unref_work; | 
| Jyri Sarha | 103cd8b | 2015-02-10 14:13:23 +0200 | [diff] [blame] | 52 |  | 
|  | 53 | /* Only set if an external encoder is connected */ | 
|  | 54 | bool simulate_vesa_sync; | 
| Jyri Sarha | 5895d08 | 2016-01-08 14:33:09 +0200 | [diff] [blame] | 55 |  | 
|  | 56 | int sync_lost_count; | 
|  | 57 | bool frame_intact; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 58 | }; | 
|  | 59 | #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) | 
|  | 60 |  | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 61 | static void unref_worker(struct drm_flip_work *work, void *val) | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 62 | { | 
| Darren Etheridge | f7b4575 | 2013-06-21 13:52:26 -0500 | [diff] [blame] | 63 | struct tilcdc_crtc *tilcdc_crtc = | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 64 | container_of(work, struct tilcdc_crtc, unref_work); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 65 | struct drm_device *dev = tilcdc_crtc->base.dev; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 66 |  | 
|  | 67 | mutex_lock(&dev->mode_config.mutex); | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 68 | drm_framebuffer_unreference(val); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 69 | mutex_unlock(&dev->mode_config.mutex); | 
|  | 70 | } | 
|  | 71 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 72 | static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 73 | { | 
|  | 74 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 75 | struct drm_device *dev = crtc->dev; | 
| Daniel Schultz | 4c268d6 | 2016-10-28 13:52:41 +0200 | [diff] [blame] | 76 | struct tilcdc_drm_private *priv = dev->dev_private; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 77 | struct drm_gem_cma_object *gem; | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 78 | dma_addr_t start, end; | 
| Jyri Sarha | 7eb9f06 | 2016-08-26 15:10:14 +0300 | [diff] [blame] | 79 | u64 dma_base_and_ceiling; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 80 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 81 | gem = drm_fb_cma_get_gem_obj(fb, 0); | 
|  | 82 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 83 | start = gem->paddr + fb->offsets[0] + | 
|  | 84 | crtc->y * fb->pitches[0] + | 
| Laurent Pinchart | 59f11a4 | 2016-10-18 01:41:14 +0300 | [diff] [blame] | 85 | crtc->x * drm_format_plane_cpp(fb->pixel_format, 0); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 86 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 87 | end = start + (crtc->mode.vdisplay * fb->pitches[0]); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 88 |  | 
| Jyri Sarha | 7eb9f06 | 2016-08-26 15:10:14 +0300 | [diff] [blame] | 89 | /* Write LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG | 
|  | 90 | * with a single insruction, if available. This should make it more | 
|  | 91 | * unlikely that LCDC would fetch the DMA addresses in the middle of | 
|  | 92 | * an update. | 
|  | 93 | */ | 
| Daniel Schultz | 4c268d6 | 2016-10-28 13:52:41 +0200 | [diff] [blame] | 94 | if (priv->rev == 1) | 
|  | 95 | end -= 1; | 
|  | 96 |  | 
|  | 97 | dma_base_and_ceiling = (u64)end << 32 | start; | 
| Jyri Sarha | 7eb9f06 | 2016-08-26 15:10:14 +0300 | [diff] [blame] | 98 | tilcdc_write64(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_base_and_ceiling); | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 99 |  | 
|  | 100 | if (tilcdc_crtc->curr_fb) | 
|  | 101 | drm_flip_work_queue(&tilcdc_crtc->unref_work, | 
|  | 102 | tilcdc_crtc->curr_fb); | 
|  | 103 |  | 
|  | 104 | tilcdc_crtc->curr_fb = fb; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 105 | } | 
|  | 106 |  | 
| Jyri Sarha | afaf833 | 2016-06-21 16:00:44 +0300 | [diff] [blame] | 107 | static void tilcdc_crtc_enable_irqs(struct drm_device *dev) | 
|  | 108 | { | 
|  | 109 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 110 |  | 
|  | 111 | tilcdc_clear_irqstatus(dev, 0xffffffff); | 
|  | 112 |  | 
|  | 113 | if (priv->rev == 1) { | 
|  | 114 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, | 
|  | 115 | LCDC_V1_UNDERFLOW_INT_ENA); | 
| Karl Beldan | 8d6c3f7 | 2016-08-23 12:57:00 +0000 | [diff] [blame] | 116 | tilcdc_set(dev, LCDC_DMA_CTRL_REG, | 
|  | 117 | LCDC_V1_END_OF_FRAME_INT_ENA); | 
| Jyri Sarha | afaf833 | 2016-06-21 16:00:44 +0300 | [diff] [blame] | 118 | } else { | 
|  | 119 | tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, | 
|  | 120 | LCDC_V2_UNDERFLOW_INT_ENA | | 
|  | 121 | LCDC_V2_END_OF_FRAME0_INT_ENA | | 
|  | 122 | LCDC_FRAME_DONE | LCDC_SYNC_LOST); | 
|  | 123 | } | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static void tilcdc_crtc_disable_irqs(struct drm_device *dev) | 
|  | 127 | { | 
|  | 128 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 129 |  | 
|  | 130 | /* disable irqs that we might have enabled: */ | 
|  | 131 | if (priv->rev == 1) { | 
|  | 132 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, | 
|  | 133 | LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); | 
|  | 134 | tilcdc_clear(dev, LCDC_DMA_CTRL_REG, | 
|  | 135 | LCDC_V1_END_OF_FRAME_INT_ENA); | 
|  | 136 | } else { | 
|  | 137 | tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, | 
|  | 138 | LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | | 
|  | 139 | LCDC_V2_END_OF_FRAME0_INT_ENA | | 
|  | 140 | LCDC_FRAME_DONE | LCDC_SYNC_LOST); | 
|  | 141 | } | 
|  | 142 | } | 
|  | 143 |  | 
| Tomi Valkeinen | 2efec4f | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 144 | static void reset(struct drm_crtc *crtc) | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 145 | { | 
|  | 146 | struct drm_device *dev = crtc->dev; | 
|  | 147 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 148 |  | 
| Tomi Valkeinen | 2efec4f | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 149 | if (priv->rev != 2) | 
|  | 150 | return; | 
|  | 151 |  | 
|  | 152 | tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | 
|  | 153 | usleep_range(250, 1000); | 
|  | 154 | tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); | 
|  | 155 | } | 
|  | 156 |  | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 157 | static void tilcdc_crtc_enable(struct drm_crtc *crtc) | 
| Tomi Valkeinen | 2efec4f | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 158 | { | 
|  | 159 | struct drm_device *dev = crtc->dev; | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 160 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 161 |  | 
| Jyri Sarha | 2e0965b | 2016-09-06 17:25:08 +0300 | [diff] [blame] | 162 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 163 | mutex_lock(&tilcdc_crtc->enable_lock); | 
|  | 164 | if (tilcdc_crtc->enabled || tilcdc_crtc->shutdown) { | 
|  | 165 | mutex_unlock(&tilcdc_crtc->enable_lock); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 166 | return; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 167 | } | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 168 |  | 
|  | 169 | pm_runtime_get_sync(dev->dev); | 
| Tomi Valkeinen | 2efec4f | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 170 |  | 
|  | 171 | reset(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 172 |  | 
| Jyri Sarha | afaf833 | 2016-06-21 16:00:44 +0300 | [diff] [blame] | 173 | tilcdc_crtc_enable_irqs(dev); | 
|  | 174 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 175 | tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 176 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); | 
|  | 177 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | 
| Jyri Sarha | d85f850e | 2016-06-15 11:16:23 +0300 | [diff] [blame] | 178 |  | 
|  | 179 | drm_crtc_vblank_on(crtc); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 180 |  | 
|  | 181 | tilcdc_crtc->enabled = true; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 182 | mutex_unlock(&tilcdc_crtc->enable_lock); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 183 | } | 
|  | 184 |  | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 185 | static void tilcdc_crtc_off(struct drm_crtc *crtc, bool shutdown) | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 186 | { | 
| Jyri Sarha | 2d5be88 | 2016-04-07 20:20:23 +0300 | [diff] [blame] | 187 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 188 | struct drm_device *dev = crtc->dev; | 
| Jyri Sarha | 2d5be88 | 2016-04-07 20:20:23 +0300 | [diff] [blame] | 189 | struct tilcdc_drm_private *priv = dev->dev_private; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 190 |  | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 191 | mutex_lock(&tilcdc_crtc->enable_lock); | 
|  | 192 | if (shutdown) | 
|  | 193 | tilcdc_crtc->shutdown = true; | 
|  | 194 | if (!tilcdc_crtc->enabled) { | 
|  | 195 | mutex_unlock(&tilcdc_crtc->enable_lock); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 196 | return; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 197 | } | 
| Jyri Sarha | 2d5be88 | 2016-04-07 20:20:23 +0300 | [diff] [blame] | 198 | tilcdc_crtc->frame_done = false; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 199 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | 
| Jyri Sarha | 2d5be88 | 2016-04-07 20:20:23 +0300 | [diff] [blame] | 200 |  | 
|  | 201 | /* | 
|  | 202 | * if necessary wait for framedone irq which will still come | 
|  | 203 | * before putting things to sleep.. | 
|  | 204 | */ | 
|  | 205 | if (priv->rev == 2) { | 
|  | 206 | int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq, | 
|  | 207 | tilcdc_crtc->frame_done, | 
| Jyri Sarha | 437c7d9 | 2016-06-16 16:19:17 +0300 | [diff] [blame] | 208 | msecs_to_jiffies(500)); | 
| Jyri Sarha | 2d5be88 | 2016-04-07 20:20:23 +0300 | [diff] [blame] | 209 | if (ret == 0) | 
|  | 210 | dev_err(dev->dev, "%s: timeout waiting for framedone\n", | 
|  | 211 | __func__); | 
|  | 212 | } | 
| Jyri Sarha | d85f850e | 2016-06-15 11:16:23 +0300 | [diff] [blame] | 213 |  | 
|  | 214 | drm_crtc_vblank_off(crtc); | 
| Jyri Sarha | afaf833 | 2016-06-21 16:00:44 +0300 | [diff] [blame] | 215 |  | 
|  | 216 | tilcdc_crtc_disable_irqs(dev); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 217 |  | 
|  | 218 | pm_runtime_put_sync(dev->dev); | 
|  | 219 |  | 
|  | 220 | if (tilcdc_crtc->next_fb) { | 
|  | 221 | drm_flip_work_queue(&tilcdc_crtc->unref_work, | 
|  | 222 | tilcdc_crtc->next_fb); | 
|  | 223 | tilcdc_crtc->next_fb = NULL; | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 | if (tilcdc_crtc->curr_fb) { | 
|  | 227 | drm_flip_work_queue(&tilcdc_crtc->unref_work, | 
|  | 228 | tilcdc_crtc->curr_fb); | 
|  | 229 | tilcdc_crtc->curr_fb = NULL; | 
|  | 230 | } | 
|  | 231 |  | 
|  | 232 | drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); | 
|  | 233 | tilcdc_crtc->last_vblank = ktime_set(0, 0); | 
|  | 234 |  | 
|  | 235 | tilcdc_crtc->enabled = false; | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 236 | mutex_unlock(&tilcdc_crtc->enable_lock); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 237 | } | 
|  | 238 |  | 
| Jyri Sarha | 9e79e06 | 2016-10-18 23:23:27 +0300 | [diff] [blame] | 239 | static void tilcdc_crtc_disable(struct drm_crtc *crtc) | 
|  | 240 | { | 
|  | 241 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 242 | tilcdc_crtc_off(crtc, false); | 
|  | 243 | } | 
|  | 244 |  | 
|  | 245 | void tilcdc_crtc_shutdown(struct drm_crtc *crtc) | 
|  | 246 | { | 
|  | 247 | tilcdc_crtc_off(crtc, true); | 
| Jyri Sarha | 9e79e06 | 2016-10-18 23:23:27 +0300 | [diff] [blame] | 248 | } | 
|  | 249 |  | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 250 | static bool tilcdc_crtc_is_on(struct drm_crtc *crtc) | 
|  | 251 | { | 
|  | 252 | return crtc->state && crtc->state->enable && crtc->state->active; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 253 | } | 
|  | 254 |  | 
|  | 255 | static void tilcdc_crtc_destroy(struct drm_crtc *crtc) | 
|  | 256 | { | 
|  | 257 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
| Jyri Sarha | 4e910c7 | 2016-09-06 22:55:33 +0300 | [diff] [blame] | 258 | struct tilcdc_drm_private *priv = crtc->dev->dev_private; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 259 |  | 
| Jyri Sarha | 6c94c71 | 2016-09-07 11:46:40 +0300 | [diff] [blame] | 260 | drm_modeset_lock_crtc(crtc, NULL); | 
| Jyri Sarha | 47bfd6c | 2016-06-22 16:27:54 +0300 | [diff] [blame] | 261 | tilcdc_crtc_disable(crtc); | 
| Jyri Sarha | 6c94c71 | 2016-09-07 11:46:40 +0300 | [diff] [blame] | 262 | drm_modeset_unlock_crtc(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 263 |  | 
| Jyri Sarha | 4e910c7 | 2016-09-06 22:55:33 +0300 | [diff] [blame] | 264 | flush_workqueue(priv->wq); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 265 |  | 
| Jyri Sarha | d66284fb | 2015-05-27 11:58:37 +0300 | [diff] [blame] | 266 | of_node_put(crtc->port); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 267 | drm_crtc_cleanup(crtc); | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 268 | drm_flip_work_cleanup(&tilcdc_crtc->unref_work); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 269 | } | 
|  | 270 |  | 
| Jyri Sarha | e0e344e | 2016-06-22 17:21:06 +0300 | [diff] [blame] | 271 | int tilcdc_crtc_update_fb(struct drm_crtc *crtc, | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 272 | struct drm_framebuffer *fb, | 
| Jyri Sarha | e0e344e | 2016-06-22 17:21:06 +0300 | [diff] [blame] | 273 | struct drm_pending_vblank_event *event) | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 274 | { | 
|  | 275 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 276 | struct drm_device *dev = crtc->dev; | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 277 | unsigned long flags; | 
| Tomi Valkeinen | 6f206e9 | 2014-02-07 17:37:07 +0000 | [diff] [blame] | 278 |  | 
| Jyri Sarha | 2e0965b | 2016-09-06 17:25:08 +0300 | [diff] [blame] | 279 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | 
|  | 280 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 281 | if (tilcdc_crtc->event) { | 
|  | 282 | dev_err(dev->dev, "already pending page flip!\n"); | 
|  | 283 | return -EBUSY; | 
|  | 284 | } | 
|  | 285 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 286 | drm_framebuffer_reference(fb); | 
|  | 287 |  | 
| Matt Roper | f4510a2 | 2014-04-01 15:22:40 -0700 | [diff] [blame] | 288 | crtc->primary->fb = fb; | 
| Tomi Valkeinen | 65734a2 | 2015-10-19 12:30:03 +0300 | [diff] [blame] | 289 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 290 | spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 291 |  | 
| Jyri Sarha | 0a1fe1b | 2016-06-13 09:53:36 +0300 | [diff] [blame] | 292 | if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) { | 
|  | 293 | ktime_t next_vblank; | 
|  | 294 | s64 tdiff; | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 295 |  | 
| Jyri Sarha | 0a1fe1b | 2016-06-13 09:53:36 +0300 | [diff] [blame] | 296 | next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, | 
|  | 297 | 1000000 / crtc->hwmode.vrefresh); | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 298 |  | 
| Jyri Sarha | 0a1fe1b | 2016-06-13 09:53:36 +0300 | [diff] [blame] | 299 | tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); | 
|  | 300 |  | 
|  | 301 | if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US) | 
|  | 302 | tilcdc_crtc->next_fb = fb; | 
|  | 303 | } | 
|  | 304 |  | 
|  | 305 | if (tilcdc_crtc->next_fb != fb) | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 306 | set_scanout(crtc, fb); | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 307 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 308 | tilcdc_crtc->event = event; | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 309 |  | 
|  | 310 | spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 311 |  | 
|  | 312 | return 0; | 
|  | 313 | } | 
|  | 314 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 315 | static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, | 
|  | 316 | const struct drm_display_mode *mode, | 
|  | 317 | struct drm_display_mode *adjusted_mode) | 
|  | 318 | { | 
| Jyri Sarha | 103cd8b | 2015-02-10 14:13:23 +0200 | [diff] [blame] | 319 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 320 |  | 
|  | 321 | if (!tilcdc_crtc->simulate_vesa_sync) | 
|  | 322 | return true; | 
|  | 323 |  | 
|  | 324 | /* | 
|  | 325 | * tilcdc does not generate VESA-compliant sync but aligns | 
|  | 326 | * VS on the second edge of HS instead of first edge. | 
|  | 327 | * We use adjusted_mode, to fixup sync by aligning both rising | 
|  | 328 | * edges and add HSKEW offset to fix the sync. | 
|  | 329 | */ | 
|  | 330 | adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; | 
|  | 331 | adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; | 
|  | 332 |  | 
|  | 333 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) { | 
|  | 334 | adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; | 
|  | 335 | adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; | 
|  | 336 | } else { | 
|  | 337 | adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; | 
|  | 338 | adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; | 
|  | 339 | } | 
|  | 340 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 341 | return true; | 
|  | 342 | } | 
|  | 343 |  | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 344 | /* | 
|  | 345 | * Calculate the percentage difference between the requested pixel clock rate | 
|  | 346 | * and the effective rate resulting from calculating the clock divider value. | 
|  | 347 | */ | 
|  | 348 | static unsigned int tilcdc_pclk_diff(unsigned long rate, | 
|  | 349 | unsigned long real_rate) | 
|  | 350 | { | 
|  | 351 | int r = rate / 100, rr = real_rate / 100; | 
|  | 352 |  | 
|  | 353 | return (unsigned int)(abs(((rr - r) * 100) / r)); | 
|  | 354 | } | 
|  | 355 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 356 | static void tilcdc_crtc_set_clk(struct drm_crtc *crtc) | 
|  | 357 | { | 
|  | 358 | struct drm_device *dev = crtc->dev; | 
|  | 359 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 360 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 361 | unsigned long clk_rate, real_rate, req_rate; | 
|  | 362 | unsigned int clkdiv; | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 363 | int ret; | 
|  | 364 |  | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 365 | clkdiv = 2; /* first try using a standard divider of 2 */ | 
|  | 366 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 367 | /* mode.clock is in KHz, set_rate wants parameter in Hz */ | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 368 | req_rate = crtc->mode.clock * 1000; | 
|  | 369 |  | 
|  | 370 | ret = clk_set_rate(priv->clk, req_rate * clkdiv); | 
|  | 371 | clk_rate = clk_get_rate(priv->clk); | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 372 | if (ret < 0) { | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 373 | /* | 
|  | 374 | * If we fail to set the clock rate (some architectures don't | 
|  | 375 | * use the common clock framework yet and may not implement | 
|  | 376 | * all the clk API calls for every clock), try the next best | 
|  | 377 | * thing: adjusting the clock divider, unless clk_get_rate() | 
|  | 378 | * failed as well. | 
|  | 379 | */ | 
|  | 380 | if (!clk_rate) { | 
|  | 381 | /* Nothing more we can do. Just bail out. */ | 
|  | 382 | dev_err(dev->dev, | 
|  | 383 | "failed to set the pixel clock - unable to read current lcdc clock rate\n"); | 
|  | 384 | return; | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | clkdiv = DIV_ROUND_CLOSEST(clk_rate, req_rate); | 
|  | 388 |  | 
|  | 389 | /* | 
|  | 390 | * Emit a warning if the real clock rate resulting from the | 
|  | 391 | * calculated divider differs much from the requested rate. | 
|  | 392 | * | 
|  | 393 | * 5% is an arbitrary value - LCDs are usually quite tolerant | 
|  | 394 | * about pixel clock rates. | 
|  | 395 | */ | 
|  | 396 | real_rate = clkdiv * req_rate; | 
|  | 397 |  | 
|  | 398 | if (tilcdc_pclk_diff(clk_rate, real_rate) > 5) { | 
|  | 399 | dev_warn(dev->dev, | 
|  | 400 | "effective pixel clock rate (%luHz) differs from the calculated rate (%luHz)\n", | 
|  | 401 | clk_rate, real_rate); | 
|  | 402 | } | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 403 | } | 
|  | 404 |  | 
| Bartosz Golaszewski | cb42e20 | 2016-09-29 18:43:57 +0200 | [diff] [blame] | 405 | tilcdc_crtc->lcd_fck_rate = clk_rate; | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 406 |  | 
|  | 407 | DBG("lcd_clk=%u, mode clock=%d, div=%u", | 
|  | 408 | tilcdc_crtc->lcd_fck_rate, crtc->mode.clock, clkdiv); | 
|  | 409 |  | 
|  | 410 | /* Configure the LCD clock divisor. */ | 
|  | 411 | tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) | | 
|  | 412 | LCDC_RASTER_MODE); | 
|  | 413 |  | 
|  | 414 | if (priv->rev == 2) | 
|  | 415 | tilcdc_set(dev, LCDC_CLK_ENABLE_REG, | 
|  | 416 | LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | | 
|  | 417 | LCDC_V2_CORE_CLK_EN); | 
|  | 418 | } | 
|  | 419 |  | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 420 | static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc) | 
|  | 421 | { | 
|  | 422 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 423 | struct drm_device *dev = crtc->dev; | 
|  | 424 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 425 | const struct tilcdc_panel_info *info = tilcdc_crtc->info; | 
|  | 426 | uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw; | 
|  | 427 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; | 
|  | 428 | struct drm_framebuffer *fb = crtc->primary->state->fb; | 
|  | 429 |  | 
| Jyri Sarha | 2e0965b | 2016-09-06 17:25:08 +0300 | [diff] [blame] | 430 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | 
|  | 431 |  | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 432 | if (WARN_ON(!info)) | 
|  | 433 | return; | 
|  | 434 |  | 
|  | 435 | if (WARN_ON(!fb)) | 
|  | 436 | return; | 
|  | 437 |  | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 438 | /* Configure the Burst Size and fifo threshold of DMA: */ | 
|  | 439 | reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770; | 
|  | 440 | switch (info->dma_burst_sz) { | 
|  | 441 | case 1: | 
|  | 442 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_1); | 
|  | 443 | break; | 
|  | 444 | case 2: | 
|  | 445 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_2); | 
|  | 446 | break; | 
|  | 447 | case 4: | 
|  | 448 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_4); | 
|  | 449 | break; | 
|  | 450 | case 8: | 
|  | 451 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_8); | 
|  | 452 | break; | 
|  | 453 | case 16: | 
|  | 454 | reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16); | 
|  | 455 | break; | 
|  | 456 | default: | 
|  | 457 | dev_err(dev->dev, "invalid burst size\n"); | 
|  | 458 | return; | 
|  | 459 | } | 
|  | 460 | reg |= (info->fifo_th << 8); | 
|  | 461 | tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg); | 
|  | 462 |  | 
|  | 463 | /* Configure timings: */ | 
|  | 464 | hbp = mode->htotal - mode->hsync_end; | 
|  | 465 | hfp = mode->hsync_start - mode->hdisplay; | 
|  | 466 | hsw = mode->hsync_end - mode->hsync_start; | 
|  | 467 | vbp = mode->vtotal - mode->vsync_end; | 
|  | 468 | vfp = mode->vsync_start - mode->vdisplay; | 
|  | 469 | vsw = mode->vsync_end - mode->vsync_start; | 
|  | 470 |  | 
|  | 471 | DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u", | 
|  | 472 | mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); | 
|  | 473 |  | 
|  | 474 | /* Set AC Bias Period and Number of Transitions per Interrupt: */ | 
|  | 475 | reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00; | 
|  | 476 | reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) | | 
|  | 477 | LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt); | 
|  | 478 |  | 
|  | 479 | /* | 
|  | 480 | * subtract one from hfp, hbp, hsw because the hardware uses | 
|  | 481 | * a value of 0 as 1 | 
|  | 482 | */ | 
|  | 483 | if (priv->rev == 2) { | 
|  | 484 | /* clear bits we're going to set */ | 
|  | 485 | reg &= ~0x78000033; | 
|  | 486 | reg |= ((hfp-1) & 0x300) >> 8; | 
|  | 487 | reg |= ((hbp-1) & 0x300) >> 4; | 
|  | 488 | reg |= ((hsw-1) & 0x3c0) << 21; | 
|  | 489 | } | 
|  | 490 | tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg); | 
|  | 491 |  | 
|  | 492 | reg = (((mode->hdisplay >> 4) - 1) << 4) | | 
|  | 493 | (((hbp-1) & 0xff) << 24) | | 
|  | 494 | (((hfp-1) & 0xff) << 16) | | 
|  | 495 | (((hsw-1) & 0x3f) << 10); | 
|  | 496 | if (priv->rev == 2) | 
|  | 497 | reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3; | 
|  | 498 | tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg); | 
|  | 499 |  | 
|  | 500 | reg = ((mode->vdisplay - 1) & 0x3ff) | | 
|  | 501 | ((vbp & 0xff) << 24) | | 
|  | 502 | ((vfp & 0xff) << 16) | | 
|  | 503 | (((vsw-1) & 0x3f) << 10); | 
|  | 504 | tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg); | 
|  | 505 |  | 
|  | 506 | /* | 
|  | 507 | * be sure to set Bit 10 for the V2 LCDC controller, | 
|  | 508 | * otherwise limited to 1024 pixels width, stopping | 
|  | 509 | * 1920x1080 being supported. | 
|  | 510 | */ | 
|  | 511 | if (priv->rev == 2) { | 
|  | 512 | if ((mode->vdisplay - 1) & 0x400) { | 
|  | 513 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, | 
|  | 514 | LCDC_LPP_B10); | 
|  | 515 | } else { | 
|  | 516 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, | 
|  | 517 | LCDC_LPP_B10); | 
|  | 518 | } | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | /* Configure display type: */ | 
|  | 522 | reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) & | 
|  | 523 | ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE | | 
|  | 524 | LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | | 
|  | 525 | 0x000ff000 /* Palette Loading Delay bits */); | 
|  | 526 | reg |= LCDC_TFT_MODE; /* no monochrome/passive support */ | 
|  | 527 | if (info->tft_alt_mode) | 
|  | 528 | reg |= LCDC_TFT_ALT_ENABLE; | 
|  | 529 | if (priv->rev == 2) { | 
| Laurent Pinchart | 59f11a4 | 2016-10-18 01:41:14 +0300 | [diff] [blame] | 530 | switch (fb->pixel_format) { | 
|  | 531 | case DRM_FORMAT_BGR565: | 
|  | 532 | case DRM_FORMAT_RGB565: | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 533 | break; | 
| Laurent Pinchart | 59f11a4 | 2016-10-18 01:41:14 +0300 | [diff] [blame] | 534 | case DRM_FORMAT_XBGR8888: | 
|  | 535 | case DRM_FORMAT_XRGB8888: | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 536 | reg |= LCDC_V2_TFT_24BPP_UNPACK; | 
|  | 537 | /* fallthrough */ | 
| Laurent Pinchart | 59f11a4 | 2016-10-18 01:41:14 +0300 | [diff] [blame] | 538 | case DRM_FORMAT_BGR888: | 
|  | 539 | case DRM_FORMAT_RGB888: | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 540 | reg |= LCDC_V2_TFT_24BPP_MODE; | 
|  | 541 | break; | 
|  | 542 | default: | 
|  | 543 | dev_err(dev->dev, "invalid pixel format\n"); | 
|  | 544 | return; | 
|  | 545 | } | 
|  | 546 | } | 
|  | 547 | reg |= info->fdd < 12; | 
|  | 548 | tilcdc_write(dev, LCDC_RASTER_CTRL_REG, reg); | 
|  | 549 |  | 
|  | 550 | if (info->invert_pxl_clk) | 
|  | 551 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | 
|  | 552 | else | 
|  | 553 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); | 
|  | 554 |  | 
|  | 555 | if (info->sync_ctrl) | 
|  | 556 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | 
|  | 557 | else | 
|  | 558 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); | 
|  | 559 |  | 
|  | 560 | if (info->sync_edge) | 
|  | 561 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | 
|  | 562 | else | 
|  | 563 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); | 
|  | 564 |  | 
|  | 565 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | 
|  | 566 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | 
|  | 567 | else | 
|  | 568 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); | 
|  | 569 |  | 
|  | 570 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | 
|  | 571 | tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | 
|  | 572 | else | 
|  | 573 | tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); | 
|  | 574 |  | 
|  | 575 | if (info->raster_order) | 
|  | 576 | tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | 
|  | 577 | else | 
|  | 578 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); | 
|  | 579 |  | 
|  | 580 | drm_framebuffer_reference(fb); | 
|  | 581 |  | 
|  | 582 | set_scanout(crtc, fb); | 
|  | 583 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 584 | tilcdc_crtc_set_clk(crtc); | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 585 |  | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 586 | crtc->hwmode = crtc->state->adjusted_mode; | 
|  | 587 | } | 
|  | 588 |  | 
| Jyri Sarha | db380c5 | 2016-04-07 15:10:23 +0300 | [diff] [blame] | 589 | static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc, | 
|  | 590 | struct drm_crtc_state *state) | 
|  | 591 | { | 
|  | 592 | struct drm_display_mode *mode = &state->mode; | 
|  | 593 | int ret; | 
|  | 594 |  | 
|  | 595 | /* If we are not active we don't care */ | 
|  | 596 | if (!state->active) | 
|  | 597 | return 0; | 
|  | 598 |  | 
|  | 599 | if (state->state->planes[0].ptr != crtc->primary || | 
|  | 600 | state->state->planes[0].state == NULL || | 
|  | 601 | state->state->planes[0].state->crtc != crtc) { | 
|  | 602 | dev_dbg(crtc->dev->dev, "CRTC primary plane must be present"); | 
|  | 603 | return -EINVAL; | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 | ret = tilcdc_crtc_mode_valid(crtc, mode); | 
|  | 607 | if (ret) { | 
|  | 608 | dev_dbg(crtc->dev->dev, "Mode \"%s\" not valid", mode->name); | 
|  | 609 | return -EINVAL; | 
|  | 610 | } | 
|  | 611 |  | 
|  | 612 | return 0; | 
|  | 613 | } | 
|  | 614 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 615 | static const struct drm_crtc_funcs tilcdc_crtc_funcs = { | 
| Jyri Sarha | 305198d | 2016-04-07 15:05:16 +0300 | [diff] [blame] | 616 | .destroy        = tilcdc_crtc_destroy, | 
|  | 617 | .set_config     = drm_atomic_helper_set_config, | 
|  | 618 | .page_flip      = drm_atomic_helper_page_flip, | 
|  | 619 | .reset		= drm_atomic_helper_crtc_reset, | 
|  | 620 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | 
|  | 621 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 622 | }; | 
|  | 623 |  | 
|  | 624 | static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 625 | .mode_fixup     = tilcdc_crtc_mode_fixup, | 
| Jyri Sarha | 305198d | 2016-04-07 15:05:16 +0300 | [diff] [blame] | 626 | .enable		= tilcdc_crtc_enable, | 
|  | 627 | .disable	= tilcdc_crtc_disable, | 
| Jyri Sarha | db380c5 | 2016-04-07 15:10:23 +0300 | [diff] [blame] | 628 | .atomic_check	= tilcdc_crtc_atomic_check, | 
| Jyri Sarha | f6382f1 | 2016-04-07 15:09:50 +0300 | [diff] [blame] | 629 | .mode_set_nofb	= tilcdc_crtc_mode_set_nofb, | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 630 | }; | 
|  | 631 |  | 
|  | 632 | int tilcdc_crtc_max_width(struct drm_crtc *crtc) | 
|  | 633 | { | 
|  | 634 | struct drm_device *dev = crtc->dev; | 
|  | 635 | struct tilcdc_drm_private *priv = dev->dev_private; | 
|  | 636 | int max_width = 0; | 
|  | 637 |  | 
|  | 638 | if (priv->rev == 1) | 
|  | 639 | max_width = 1024; | 
|  | 640 | else if (priv->rev == 2) | 
|  | 641 | max_width = 2048; | 
|  | 642 |  | 
|  | 643 | return max_width; | 
|  | 644 | } | 
|  | 645 |  | 
|  | 646 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode) | 
|  | 647 | { | 
|  | 648 | struct tilcdc_drm_private *priv = crtc->dev->dev_private; | 
|  | 649 | unsigned int bandwidth; | 
| Darren Etheridge | e1c5d0a | 2013-06-21 13:52:25 -0500 | [diff] [blame] | 650 | uint32_t hbp, hfp, hsw, vbp, vfp, vsw; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 651 |  | 
| Darren Etheridge | e1c5d0a | 2013-06-21 13:52:25 -0500 | [diff] [blame] | 652 | /* | 
|  | 653 | * check to see if the width is within the range that | 
|  | 654 | * the LCD Controller physically supports | 
|  | 655 | */ | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 656 | if (mode->hdisplay > tilcdc_crtc_max_width(crtc)) | 
|  | 657 | return MODE_VIRTUAL_X; | 
|  | 658 |  | 
|  | 659 | /* width must be multiple of 16 */ | 
|  | 660 | if (mode->hdisplay & 0xf) | 
|  | 661 | return MODE_VIRTUAL_X; | 
|  | 662 |  | 
|  | 663 | if (mode->vdisplay > 2048) | 
|  | 664 | return MODE_VIRTUAL_Y; | 
|  | 665 |  | 
| Darren Etheridge | e1c5d0a | 2013-06-21 13:52:25 -0500 | [diff] [blame] | 666 | DBG("Processing mode %dx%d@%d with pixel clock %d", | 
|  | 667 | mode->hdisplay, mode->vdisplay, | 
|  | 668 | drm_mode_vrefresh(mode), mode->clock); | 
|  | 669 |  | 
|  | 670 | hbp = mode->htotal - mode->hsync_end; | 
|  | 671 | hfp = mode->hsync_start - mode->hdisplay; | 
|  | 672 | hsw = mode->hsync_end - mode->hsync_start; | 
|  | 673 | vbp = mode->vtotal - mode->vsync_end; | 
|  | 674 | vfp = mode->vsync_start - mode->vdisplay; | 
|  | 675 | vsw = mode->vsync_end - mode->vsync_start; | 
|  | 676 |  | 
|  | 677 | if ((hbp-1) & ~0x3ff) { | 
|  | 678 | DBG("Pruning mode: Horizontal Back Porch out of range"); | 
|  | 679 | return MODE_HBLANK_WIDE; | 
|  | 680 | } | 
|  | 681 |  | 
|  | 682 | if ((hfp-1) & ~0x3ff) { | 
|  | 683 | DBG("Pruning mode: Horizontal Front Porch out of range"); | 
|  | 684 | return MODE_HBLANK_WIDE; | 
|  | 685 | } | 
|  | 686 |  | 
|  | 687 | if ((hsw-1) & ~0x3ff) { | 
|  | 688 | DBG("Pruning mode: Horizontal Sync Width out of range"); | 
|  | 689 | return MODE_HSYNC_WIDE; | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | if (vbp & ~0xff) { | 
|  | 693 | DBG("Pruning mode: Vertical Back Porch out of range"); | 
|  | 694 | return MODE_VBLANK_WIDE; | 
|  | 695 | } | 
|  | 696 |  | 
|  | 697 | if (vfp & ~0xff) { | 
|  | 698 | DBG("Pruning mode: Vertical Front Porch out of range"); | 
|  | 699 | return MODE_VBLANK_WIDE; | 
|  | 700 | } | 
|  | 701 |  | 
|  | 702 | if ((vsw-1) & ~0x3f) { | 
|  | 703 | DBG("Pruning mode: Vertical Sync Width out of range"); | 
|  | 704 | return MODE_VSYNC_WIDE; | 
|  | 705 | } | 
|  | 706 |  | 
| Darren Etheridge | 4e56434 | 2013-06-21 13:52:23 -0500 | [diff] [blame] | 707 | /* | 
|  | 708 | * some devices have a maximum allowed pixel clock | 
|  | 709 | * configured from the DT | 
|  | 710 | */ | 
|  | 711 | if (mode->clock > priv->max_pixelclock) { | 
| Darren Etheridge | f7b4575 | 2013-06-21 13:52:26 -0500 | [diff] [blame] | 712 | DBG("Pruning mode: pixel clock too high"); | 
| Darren Etheridge | 4e56434 | 2013-06-21 13:52:23 -0500 | [diff] [blame] | 713 | return MODE_CLOCK_HIGH; | 
|  | 714 | } | 
|  | 715 |  | 
|  | 716 | /* | 
|  | 717 | * some devices further limit the max horizontal resolution | 
|  | 718 | * configured from the DT | 
|  | 719 | */ | 
|  | 720 | if (mode->hdisplay > priv->max_width) | 
|  | 721 | return MODE_BAD_WIDTH; | 
|  | 722 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 723 | /* filter out modes that would require too much memory bandwidth: */ | 
| Darren Etheridge | 4e56434 | 2013-06-21 13:52:23 -0500 | [diff] [blame] | 724 | bandwidth = mode->hdisplay * mode->vdisplay * | 
|  | 725 | drm_mode_vrefresh(mode); | 
|  | 726 | if (bandwidth > priv->max_bandwidth) { | 
| Darren Etheridge | f7b4575 | 2013-06-21 13:52:26 -0500 | [diff] [blame] | 727 | DBG("Pruning mode: exceeds defined bandwidth limit"); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 728 | return MODE_BAD; | 
| Darren Etheridge | 4e56434 | 2013-06-21 13:52:23 -0500 | [diff] [blame] | 729 | } | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 730 |  | 
|  | 731 | return MODE_OK; | 
|  | 732 | } | 
|  | 733 |  | 
|  | 734 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | 
|  | 735 | const struct tilcdc_panel_info *info) | 
|  | 736 | { | 
|  | 737 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 738 | tilcdc_crtc->info = info; | 
|  | 739 | } | 
|  | 740 |  | 
| Jyri Sarha | 103cd8b | 2015-02-10 14:13:23 +0200 | [diff] [blame] | 741 | void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, | 
|  | 742 | bool simulate_vesa_sync) | 
|  | 743 | { | 
|  | 744 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 745 |  | 
|  | 746 | tilcdc_crtc->simulate_vesa_sync = simulate_vesa_sync; | 
|  | 747 | } | 
|  | 748 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 749 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc) | 
|  | 750 | { | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 751 | struct drm_device *dev = crtc->dev; | 
|  | 752 | struct tilcdc_drm_private *priv = dev->dev_private; | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 753 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 754 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 755 | drm_modeset_lock_crtc(crtc, NULL); | 
|  | 756 | if (tilcdc_crtc->lcd_fck_rate != clk_get_rate(priv->clk)) { | 
|  | 757 | if (tilcdc_crtc_is_on(crtc)) { | 
|  | 758 | pm_runtime_get_sync(dev->dev); | 
|  | 759 | tilcdc_crtc_disable(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 760 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 761 | tilcdc_crtc_set_clk(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 762 |  | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 763 | tilcdc_crtc_enable(crtc); | 
|  | 764 | pm_runtime_put_sync(dev->dev); | 
|  | 765 | } | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 766 | } | 
| Jyri Sarha | 642e516 | 2016-09-06 16:19:54 +0300 | [diff] [blame] | 767 | drm_modeset_unlock_crtc(crtc); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 768 | } | 
|  | 769 |  | 
| Jyri Sarha | 5895d08 | 2016-01-08 14:33:09 +0200 | [diff] [blame] | 770 | #define SYNC_LOST_COUNT_LIMIT 50 | 
|  | 771 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 772 | irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) | 
|  | 773 | { | 
|  | 774 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 
|  | 775 | struct drm_device *dev = crtc->dev; | 
|  | 776 | struct tilcdc_drm_private *priv = dev->dev_private; | 
| Tomi Valkeinen | 317aae7 | 2015-10-20 12:08:03 +0300 | [diff] [blame] | 777 | uint32_t stat; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 778 |  | 
| Tomi Valkeinen | 317aae7 | 2015-10-20 12:08:03 +0300 | [diff] [blame] | 779 | stat = tilcdc_read_irqstatus(dev); | 
|  | 780 | tilcdc_clear_irqstatus(dev, stat); | 
|  | 781 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 782 | if (stat & LCDC_END_OF_FRAME0) { | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 783 | unsigned long flags; | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 784 | bool skip_event = false; | 
|  | 785 | ktime_t now; | 
|  | 786 |  | 
|  | 787 | now = ktime_get(); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 788 |  | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 789 | drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 790 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 791 | spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 792 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 793 | tilcdc_crtc->last_vblank = now; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 794 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 795 | if (tilcdc_crtc->next_fb) { | 
|  | 796 | set_scanout(crtc, tilcdc_crtc->next_fb); | 
|  | 797 | tilcdc_crtc->next_fb = NULL; | 
|  | 798 | skip_event = true; | 
| Tomi Valkeinen | 2b2080d | 2015-10-20 09:37:27 +0300 | [diff] [blame] | 799 | } | 
|  | 800 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 801 | spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); | 
|  | 802 |  | 
| Gustavo Padovan | 099ede8 | 2016-07-04 21:04:52 -0300 | [diff] [blame] | 803 | drm_crtc_handle_vblank(crtc); | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 804 |  | 
|  | 805 | if (!skip_event) { | 
|  | 806 | struct drm_pending_vblank_event *event; | 
|  | 807 |  | 
|  | 808 | spin_lock_irqsave(&dev->event_lock, flags); | 
|  | 809 |  | 
|  | 810 | event = tilcdc_crtc->event; | 
|  | 811 | tilcdc_crtc->event = NULL; | 
|  | 812 | if (event) | 
| Gustavo Padovan | dfebc15 | 2016-04-14 10:48:22 -0700 | [diff] [blame] | 813 | drm_crtc_send_vblank_event(crtc, event); | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 814 |  | 
|  | 815 | spin_unlock_irqrestore(&dev->event_lock, flags); | 
|  | 816 | } | 
| Jyri Sarha | 5895d08 | 2016-01-08 14:33:09 +0200 | [diff] [blame] | 817 |  | 
|  | 818 | if (tilcdc_crtc->frame_intact) | 
|  | 819 | tilcdc_crtc->sync_lost_count = 0; | 
|  | 820 | else | 
|  | 821 | tilcdc_crtc->frame_intact = true; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 822 | } | 
|  | 823 |  | 
| Jyri Sarha | 1494411 | 2016-04-07 20:36:48 +0300 | [diff] [blame] | 824 | if (stat & LCDC_FIFO_UNDERFLOW) | 
| Daniel Schultz | d701453 | 2016-10-28 13:52:42 +0200 | [diff] [blame] | 825 | dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underflow", | 
| Jyri Sarha | 1494411 | 2016-04-07 20:36:48 +0300 | [diff] [blame] | 826 | __func__, stat); | 
|  | 827 |  | 
|  | 828 | /* For revision 2 only */ | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 829 | if (priv->rev == 2) { | 
|  | 830 | if (stat & LCDC_FRAME_DONE) { | 
|  | 831 | tilcdc_crtc->frame_done = true; | 
|  | 832 | wake_up(&tilcdc_crtc->frame_done_wq); | 
|  | 833 | } | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 834 |  | 
| Jyri Sarha | 1abcdac | 2016-06-17 11:54:06 +0300 | [diff] [blame] | 835 | if (stat & LCDC_SYNC_LOST) { | 
|  | 836 | dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", | 
|  | 837 | __func__, stat); | 
|  | 838 | tilcdc_crtc->frame_intact = false; | 
|  | 839 | if (tilcdc_crtc->sync_lost_count++ > | 
|  | 840 | SYNC_LOST_COUNT_LIMIT) { | 
|  | 841 | dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, disabling the interrupt", __func__, stat); | 
|  | 842 | tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, | 
|  | 843 | LCDC_SYNC_LOST); | 
|  | 844 | } | 
| Jyri Sarha | 5895d08 | 2016-01-08 14:33:09 +0200 | [diff] [blame] | 845 | } | 
| Jyri Sarha | c0c2baa | 2015-12-18 13:07:52 +0200 | [diff] [blame] | 846 |  | 
| Jyri Sarha | 1494411 | 2016-04-07 20:36:48 +0300 | [diff] [blame] | 847 | /* Indicate to LCDC that the interrupt service routine has | 
|  | 848 | * completed, see 13.3.6.1.6 in AM335x TRM. | 
|  | 849 | */ | 
|  | 850 | tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); | 
|  | 851 | } | 
| Jyri Sarha | c0c2baa | 2015-12-18 13:07:52 +0200 | [diff] [blame] | 852 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 853 | return IRQ_HANDLED; | 
|  | 854 | } | 
|  | 855 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 856 | struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) | 
|  | 857 | { | 
| Jyri Sarha | d66284fb | 2015-05-27 11:58:37 +0300 | [diff] [blame] | 858 | struct tilcdc_drm_private *priv = dev->dev_private; | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 859 | struct tilcdc_crtc *tilcdc_crtc; | 
|  | 860 | struct drm_crtc *crtc; | 
|  | 861 | int ret; | 
|  | 862 |  | 
| Jyri Sarha | d0ec32c | 2016-02-23 12:44:27 +0200 | [diff] [blame] | 863 | tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 864 | if (!tilcdc_crtc) { | 
|  | 865 | dev_err(dev->dev, "allocation failed\n"); | 
|  | 866 | return NULL; | 
|  | 867 | } | 
|  | 868 |  | 
|  | 869 | crtc = &tilcdc_crtc->base; | 
|  | 870 |  | 
| Jyri Sarha | 47f571c | 2016-04-07 15:04:18 +0300 | [diff] [blame] | 871 | ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary); | 
|  | 872 | if (ret < 0) | 
|  | 873 | goto fail; | 
|  | 874 |  | 
| Jyri Sarha | 2d53a18 | 2016-10-25 12:27:31 +0300 | [diff] [blame^] | 875 | mutex_init(&tilcdc_crtc->enable_lock); | 
|  | 876 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 877 | init_waitqueue_head(&tilcdc_crtc->frame_done_wq); | 
|  | 878 |  | 
| Boris BREZILLON | d7f8db5 | 2014-11-14 19:30:30 +0100 | [diff] [blame] | 879 | drm_flip_work_init(&tilcdc_crtc->unref_work, | 
| Rob Clark | a464d61 | 2013-08-07 13:41:20 -0400 | [diff] [blame] | 880 | "unref", unref_worker); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 881 |  | 
| Tomi Valkeinen | 2b3a8cd | 2015-11-03 12:00:51 +0200 | [diff] [blame] | 882 | spin_lock_init(&tilcdc_crtc->irq_lock); | 
|  | 883 |  | 
| Jyri Sarha | 47f571c | 2016-04-07 15:04:18 +0300 | [diff] [blame] | 884 | ret = drm_crtc_init_with_planes(dev, crtc, | 
|  | 885 | &tilcdc_crtc->primary, | 
|  | 886 | NULL, | 
|  | 887 | &tilcdc_crtc_funcs, | 
|  | 888 | "tilcdc crtc"); | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 889 | if (ret < 0) | 
|  | 890 | goto fail; | 
|  | 891 |  | 
|  | 892 | drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); | 
|  | 893 |  | 
| Jyri Sarha | d66284fb | 2015-05-27 11:58:37 +0300 | [diff] [blame] | 894 | if (priv->is_componentized) { | 
|  | 895 | struct device_node *ports = | 
|  | 896 | of_get_child_by_name(dev->dev->of_node, "ports"); | 
|  | 897 |  | 
|  | 898 | if (ports) { | 
|  | 899 | crtc->port = of_get_child_by_name(ports, "port"); | 
|  | 900 | of_node_put(ports); | 
|  | 901 | } else { | 
|  | 902 | crtc->port = | 
|  | 903 | of_get_child_by_name(dev->dev->of_node, "port"); | 
|  | 904 | } | 
|  | 905 | if (!crtc->port) { /* This should never happen */ | 
|  | 906 | dev_err(dev->dev, "Port node not found in %s\n", | 
|  | 907 | dev->dev->of_node->full_name); | 
|  | 908 | goto fail; | 
|  | 909 | } | 
|  | 910 | } | 
|  | 911 |  | 
| Rob Clark | 16ea975 | 2013-01-08 15:04:28 -0600 | [diff] [blame] | 912 | return crtc; | 
|  | 913 |  | 
|  | 914 | fail: | 
|  | 915 | tilcdc_crtc_destroy(crtc); | 
|  | 916 | return NULL; | 
|  | 917 | } |