1 /* 2 * Copyright (C) 2014 Traphandler 3 * Copyright (C) 2014 Free Electrons 4 * 5 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published by 10 * the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include <linux/clk.h> 22 #include <linux/pm.h> 23 #include <linux/pm_runtime.h> 24 25 #include <drm/drm_crtc.h> 26 #include <drm/drm_crtc_helper.h> 27 #include <drm/drmP.h> 28 29 #include <video/videomode.h> 30 31 #include "atmel_hlcdc_dc.h" 32 33 /** 34 * Atmel HLCDC CRTC structure 35 * 36 * @base: base DRM CRTC structure 37 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device 38 * @event: pointer to the current page flip event 39 * @id: CRTC id (returned by drm_crtc_index) 40 * @dpms: DPMS mode 41 */ 42 struct atmel_hlcdc_crtc { 43 struct drm_crtc base; 44 struct atmel_hlcdc_dc *dc; 45 struct drm_pending_vblank_event *event; 46 int id; 47 int dpms; 48 }; 49 50 static inline struct atmel_hlcdc_crtc * 51 drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) 52 { 53 return container_of(crtc, struct atmel_hlcdc_crtc, base); 54 } 55 56 static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode) 57 { 58 struct drm_device *dev = c->dev; 59 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 60 struct regmap *regmap = crtc->dc->hlcdc->regmap; 61 unsigned int status; 62 63 if (mode != DRM_MODE_DPMS_ON) 64 mode = DRM_MODE_DPMS_OFF; 65 66 if (crtc->dpms == mode) 67 return; 68 69 pm_runtime_get_sync(dev->dev); 70 71 if (mode != DRM_MODE_DPMS_ON) { 72 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); 73 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 74 (status & ATMEL_HLCDC_DISP)) 75 cpu_relax(); 76 77 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); 78 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 79 (status & ATMEL_HLCDC_SYNC)) 80 cpu_relax(); 81 82 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); 83 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 84 (status & ATMEL_HLCDC_PIXEL_CLK)) 85 cpu_relax(); 86 87 clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 88 89 pm_runtime_allow(dev->dev); 90 } else { 91 pm_runtime_forbid(dev->dev); 92 93 clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 94 95 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); 96 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 97 !(status & ATMEL_HLCDC_PIXEL_CLK)) 98 cpu_relax(); 99 100 101 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); 102 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 103 !(status & ATMEL_HLCDC_SYNC)) 104 cpu_relax(); 105 106 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); 107 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 108 !(status & ATMEL_HLCDC_DISP)) 109 cpu_relax(); 110 } 111 112 pm_runtime_put_sync(dev->dev); 113 114 crtc->dpms = mode; 115 } 116 117 static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c, 118 struct drm_display_mode *mode, 119 struct drm_display_mode *adj, 120 int x, int y, 121 struct drm_framebuffer *old_fb) 122 { 123 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 124 struct regmap *regmap = crtc->dc->hlcdc->regmap; 125 struct drm_plane *plane = c->primary; 126 struct drm_framebuffer *fb; 127 unsigned long mode_rate; 128 struct videomode vm; 129 unsigned long prate; 130 unsigned int cfg; 131 int div; 132 133 if (atmel_hlcdc_dc_mode_valid(crtc->dc, adj) != MODE_OK) 134 return -EINVAL; 135 136 vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; 137 vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; 138 vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; 139 vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; 140 vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; 141 vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; 142 143 regmap_write(regmap, ATMEL_HLCDC_CFG(1), 144 (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); 145 146 regmap_write(regmap, ATMEL_HLCDC_CFG(2), 147 (vm.vfront_porch - 1) | (vm.vback_porch << 16)); 148 149 regmap_write(regmap, ATMEL_HLCDC_CFG(3), 150 (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); 151 152 regmap_write(regmap, ATMEL_HLCDC_CFG(4), 153 (adj->crtc_hdisplay - 1) | 154 ((adj->crtc_vdisplay - 1) << 16)); 155 156 cfg = ATMEL_HLCDC_CLKPOL; 157 158 prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); 159 mode_rate = mode->crtc_clock * 1000; 160 if ((prate / 2) < mode_rate) { 161 prate *= 2; 162 cfg |= ATMEL_HLCDC_CLKSEL; 163 } 164 165 div = DIV_ROUND_UP(prate, mode_rate); 166 if (div < 2) 167 div = 2; 168 169 cfg |= ATMEL_HLCDC_CLKDIV(div); 170 171 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), 172 ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | 173 ATMEL_HLCDC_CLKPOL, cfg); 174 175 cfg = 0; 176 177 if (mode->flags & DRM_MODE_FLAG_NVSYNC) 178 cfg |= ATMEL_HLCDC_VSPOL; 179 180 if (mode->flags & DRM_MODE_FLAG_NHSYNC) 181 cfg |= ATMEL_HLCDC_HSPOL; 182 183 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), 184 ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | 185 ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | 186 ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | 187 ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | 188 ATMEL_HLCDC_GUARDTIME_MASK, 189 cfg); 190 191 fb = plane->fb; 192 plane->fb = old_fb; 193 194 return atmel_hlcdc_plane_update_with_mode(plane, c, fb, 0, 0, 195 adj->hdisplay, adj->vdisplay, 196 x << 16, y << 16, 197 adj->hdisplay << 16, 198 adj->vdisplay << 16, 199 adj); 200 } 201 202 int atmel_hlcdc_crtc_mode_set_base(struct drm_crtc *c, int x, int y, 203 struct drm_framebuffer *old_fb) 204 { 205 struct drm_plane *plane = c->primary; 206 struct drm_framebuffer *fb = plane->fb; 207 struct drm_display_mode *mode = &c->hwmode; 208 209 plane->fb = old_fb; 210 211 return plane->funcs->update_plane(plane, c, fb, 212 0, 0, 213 mode->hdisplay, 214 mode->vdisplay, 215 x << 16, y << 16, 216 mode->hdisplay << 16, 217 mode->vdisplay << 16); 218 } 219 220 static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc) 221 { 222 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 223 } 224 225 static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc) 226 { 227 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); 228 } 229 230 static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc, 231 const struct drm_display_mode *mode, 232 struct drm_display_mode *adjusted_mode) 233 { 234 return true; 235 } 236 237 static void atmel_hlcdc_crtc_disable(struct drm_crtc *crtc) 238 { 239 struct drm_plane *plane; 240 241 atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); 242 crtc->primary->funcs->disable_plane(crtc->primary); 243 244 drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) { 245 if (plane->crtc != crtc) 246 continue; 247 248 plane->funcs->disable_plane(crtc->primary); 249 plane->crtc = NULL; 250 } 251 } 252 253 static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { 254 .mode_fixup = atmel_hlcdc_crtc_mode_fixup, 255 .dpms = atmel_hlcdc_crtc_dpms, 256 .mode_set = atmel_hlcdc_crtc_mode_set, 257 .mode_set_base = atmel_hlcdc_crtc_mode_set_base, 258 .prepare = atmel_hlcdc_crtc_prepare, 259 .commit = atmel_hlcdc_crtc_commit, 260 .disable = atmel_hlcdc_crtc_disable, 261 }; 262 263 static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) 264 { 265 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 266 267 drm_crtc_cleanup(c); 268 kfree(crtc); 269 } 270 271 void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c, 272 struct drm_file *file) 273 { 274 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 275 struct drm_pending_vblank_event *event; 276 struct drm_device *dev = c->dev; 277 unsigned long flags; 278 279 spin_lock_irqsave(&dev->event_lock, flags); 280 event = crtc->event; 281 if (event && event->base.file_priv == file) { 282 event->base.destroy(&event->base); 283 drm_vblank_put(dev, crtc->id); 284 crtc->event = NULL; 285 } 286 spin_unlock_irqrestore(&dev->event_lock, flags); 287 } 288 289 static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) 290 { 291 struct drm_device *dev = crtc->base.dev; 292 unsigned long flags; 293 294 spin_lock_irqsave(&dev->event_lock, flags); 295 if (crtc->event) { 296 drm_send_vblank_event(dev, crtc->id, crtc->event); 297 drm_vblank_put(dev, crtc->id); 298 crtc->event = NULL; 299 } 300 spin_unlock_irqrestore(&dev->event_lock, flags); 301 } 302 303 void atmel_hlcdc_crtc_irq(struct drm_crtc *c) 304 { 305 drm_handle_vblank(c->dev, 0); 306 atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); 307 } 308 309 static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c, 310 struct drm_framebuffer *fb, 311 struct drm_pending_vblank_event *event, 312 uint32_t page_flip_flags) 313 { 314 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 315 struct atmel_hlcdc_plane_update_req req; 316 struct drm_plane *plane = c->primary; 317 struct drm_device *dev = c->dev; 318 unsigned long flags; 319 int ret = 0; 320 321 spin_lock_irqsave(&dev->event_lock, flags); 322 if (crtc->event) 323 ret = -EBUSY; 324 spin_unlock_irqrestore(&dev->event_lock, flags); 325 326 if (ret) 327 return ret; 328 329 memset(&req, 0, sizeof(req)); 330 req.crtc_x = 0; 331 req.crtc_y = 0; 332 req.crtc_h = c->mode.crtc_vdisplay; 333 req.crtc_w = c->mode.crtc_hdisplay; 334 req.src_x = c->x << 16; 335 req.src_y = c->y << 16; 336 req.src_w = req.crtc_w << 16; 337 req.src_h = req.crtc_h << 16; 338 req.fb = fb; 339 340 ret = atmel_hlcdc_plane_prepare_update_req(plane, &req, &c->hwmode); 341 if (ret) 342 return ret; 343 344 if (event) { 345 drm_vblank_get(c->dev, crtc->id); 346 spin_lock_irqsave(&dev->event_lock, flags); 347 crtc->event = event; 348 spin_unlock_irqrestore(&dev->event_lock, flags); 349 } 350 351 ret = atmel_hlcdc_plane_apply_update_req(plane, &req); 352 if (ret) 353 crtc->event = NULL; 354 else 355 plane->fb = fb; 356 357 return ret; 358 } 359 360 static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { 361 .page_flip = atmel_hlcdc_crtc_page_flip, 362 .set_config = drm_crtc_helper_set_config, 363 .destroy = atmel_hlcdc_crtc_destroy, 364 }; 365 366 int atmel_hlcdc_crtc_create(struct drm_device *dev) 367 { 368 struct atmel_hlcdc_dc *dc = dev->dev_private; 369 struct atmel_hlcdc_planes *planes = dc->planes; 370 struct atmel_hlcdc_crtc *crtc; 371 int ret; 372 int i; 373 374 crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 375 if (!crtc) 376 return -ENOMEM; 377 378 crtc->dpms = DRM_MODE_DPMS_OFF; 379 crtc->dc = dc; 380 381 ret = drm_crtc_init_with_planes(dev, &crtc->base, 382 &planes->primary->base, 383 planes->cursor ? &planes->cursor->base : NULL, 384 &atmel_hlcdc_crtc_funcs); 385 if (ret < 0) 386 goto fail; 387 388 crtc->id = drm_crtc_index(&crtc->base); 389 390 if (planes->cursor) 391 planes->cursor->base.possible_crtcs = 1 << crtc->id; 392 393 for (i = 0; i < planes->noverlays; i++) 394 planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; 395 396 drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); 397 398 dc->crtc = &crtc->base; 399 400 return 0; 401 402 fail: 403 atmel_hlcdc_crtc_destroy(&crtc->base); 404 return ret; 405 } 406 407