1 /* 2 * Copyright (C) 2012 Samsung Electronics Co.Ltd 3 * Authors: 4 * YoungJun Cho <yj44.cho@samsung.com> 5 * Eunchul Kim <chulspro.kim@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundationr 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/component.h> 14 #include <linux/err.h> 15 #include <linux/interrupt.h> 16 #include <linux/io.h> 17 #include <linux/platform_device.h> 18 #include <linux/clk.h> 19 #include <linux/of_device.h> 20 #include <linux/pm_runtime.h> 21 22 #include <drm/drmP.h> 23 #include <drm/exynos_drm.h> 24 #include "regs-rotator.h" 25 #include "exynos_drm_drv.h" 26 #include "exynos_drm_iommu.h" 27 #include "exynos_drm_ipp.h" 28 29 /* 30 * Rotator supports image crop/rotator and input/output DMA operations. 31 * input DMA reads image data from the memory. 32 * output DMA writes image data to memory. 33 */ 34 35 #define ROTATOR_AUTOSUSPEND_DELAY 2000 36 37 #define rot_read(offset) readl(rot->regs + (offset)) 38 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset)) 39 40 enum rot_irq_status { 41 ROT_IRQ_STATUS_COMPLETE = 8, 42 ROT_IRQ_STATUS_ILLEGAL = 9, 43 }; 44 45 struct rot_variant { 46 const struct exynos_drm_ipp_formats *formats; 47 unsigned int num_formats; 48 }; 49 50 /* 51 * A structure of rotator context. 52 * @ippdrv: prepare initialization using ippdrv. 53 * @regs: memory mapped io registers. 54 * @clock: rotator gate clock. 55 * @limit_tbl: limitation of rotator. 56 * @irq: irq number. 57 */ 58 struct rot_context { 59 struct exynos_drm_ipp ipp; 60 struct drm_device *drm_dev; 61 struct device *dev; 62 void __iomem *regs; 63 struct clk *clock; 64 const struct exynos_drm_ipp_formats *formats; 65 unsigned int num_formats; 66 struct exynos_drm_ipp_task *task; 67 }; 68 69 static void rotator_reg_set_irq(struct rot_context *rot, bool enable) 70 { 71 u32 val = rot_read(ROT_CONFIG); 72 73 if (enable == true) 74 val |= ROT_CONFIG_IRQ; 75 else 76 val &= ~ROT_CONFIG_IRQ; 77 78 rot_write(val, ROT_CONFIG); 79 } 80 81 static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) 82 { 83 u32 val = rot_read(ROT_STATUS); 84 85 val = ROT_STATUS_IRQ(val); 86 87 if (val == ROT_STATUS_IRQ_VAL_COMPLETE) 88 return ROT_IRQ_STATUS_COMPLETE; 89 90 return ROT_IRQ_STATUS_ILLEGAL; 91 } 92 93 static irqreturn_t rotator_irq_handler(int irq, void *arg) 94 { 95 struct rot_context *rot = arg; 96 enum rot_irq_status irq_status; 97 u32 val; 98 99 /* Get execution result */ 100 irq_status = rotator_reg_get_irq_status(rot); 101 102 /* clear status */ 103 val = rot_read(ROT_STATUS); 104 val |= ROT_STATUS_IRQ_PENDING((u32)irq_status); 105 rot_write(val, ROT_STATUS); 106 107 if (rot->task) { 108 struct exynos_drm_ipp_task *task = rot->task; 109 110 rot->task = NULL; 111 pm_runtime_mark_last_busy(rot->dev); 112 pm_runtime_put_autosuspend(rot->dev); 113 exynos_drm_ipp_task_done(task, 114 irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL); 115 } 116 117 return IRQ_HANDLED; 118 } 119 120 static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt) 121 { 122 u32 val; 123 124 val = rot_read(ROT_CONTROL); 125 val &= ~ROT_CONTROL_FMT_MASK; 126 127 switch (fmt) { 128 case DRM_FORMAT_NV12: 129 val |= ROT_CONTROL_FMT_YCBCR420_2P; 130 break; 131 case DRM_FORMAT_XRGB8888: 132 val |= ROT_CONTROL_FMT_RGB888; 133 break; 134 } 135 136 rot_write(val, ROT_CONTROL); 137 } 138 139 static void rotator_src_set_buf(struct rot_context *rot, 140 struct exynos_drm_ipp_buffer *buf) 141 { 142 u32 val; 143 144 /* Set buffer size configuration */ 145 val = ROT_SET_BUF_SIZE_H(buf->buf.height) | 146 ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]); 147 rot_write(val, ROT_SRC_BUF_SIZE); 148 149 /* Set crop image position configuration */ 150 val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x); 151 rot_write(val, ROT_SRC_CROP_POS); 152 val = ROT_SRC_CROP_SIZE_H(buf->rect.h) | 153 ROT_SRC_CROP_SIZE_W(buf->rect.w); 154 rot_write(val, ROT_SRC_CROP_SIZE); 155 156 /* Set buffer DMA address */ 157 rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0)); 158 rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1)); 159 } 160 161 static void rotator_dst_set_transf(struct rot_context *rot, 162 unsigned int rotation) 163 { 164 u32 val; 165 166 /* Set transform configuration */ 167 val = rot_read(ROT_CONTROL); 168 val &= ~ROT_CONTROL_FLIP_MASK; 169 170 if (rotation & DRM_MODE_REFLECT_X) 171 val |= ROT_CONTROL_FLIP_VERTICAL; 172 if (rotation & DRM_MODE_REFLECT_Y) 173 val |= ROT_CONTROL_FLIP_HORIZONTAL; 174 175 val &= ~ROT_CONTROL_ROT_MASK; 176 177 if (rotation & DRM_MODE_ROTATE_90) 178 val |= ROT_CONTROL_ROT_90; 179 else if (rotation & DRM_MODE_ROTATE_180) 180 val |= ROT_CONTROL_ROT_180; 181 else if (rotation & DRM_MODE_ROTATE_270) 182 val |= ROT_CONTROL_ROT_270; 183 184 rot_write(val, ROT_CONTROL); 185 } 186 187 static void rotator_dst_set_buf(struct rot_context *rot, 188 struct exynos_drm_ipp_buffer *buf) 189 { 190 u32 val; 191 192 /* Set buffer size configuration */ 193 val = ROT_SET_BUF_SIZE_H(buf->buf.height) | 194 ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]); 195 rot_write(val, ROT_DST_BUF_SIZE); 196 197 /* Set crop image position configuration */ 198 val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x); 199 rot_write(val, ROT_DST_CROP_POS); 200 201 /* Set buffer DMA address */ 202 rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0)); 203 rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1)); 204 } 205 206 static void rotator_start(struct rot_context *rot) 207 { 208 u32 val; 209 210 /* Set interrupt enable */ 211 rotator_reg_set_irq(rot, true); 212 213 val = rot_read(ROT_CONTROL); 214 val |= ROT_CONTROL_START; 215 rot_write(val, ROT_CONTROL); 216 } 217 218 static int rotator_commit(struct exynos_drm_ipp *ipp, 219 struct exynos_drm_ipp_task *task) 220 { 221 struct rot_context *rot = 222 container_of(ipp, struct rot_context, ipp); 223 224 pm_runtime_get_sync(rot->dev); 225 rot->task = task; 226 227 rotator_src_set_fmt(rot, task->src.buf.fourcc); 228 rotator_src_set_buf(rot, &task->src); 229 rotator_dst_set_transf(rot, task->transform.rotation); 230 rotator_dst_set_buf(rot, &task->dst); 231 rotator_start(rot); 232 233 return 0; 234 } 235 236 static const struct exynos_drm_ipp_funcs ipp_funcs = { 237 .commit = rotator_commit, 238 }; 239 240 static int rotator_bind(struct device *dev, struct device *master, void *data) 241 { 242 struct rot_context *rot = dev_get_drvdata(dev); 243 struct drm_device *drm_dev = data; 244 struct exynos_drm_ipp *ipp = &rot->ipp; 245 246 rot->drm_dev = drm_dev; 247 drm_iommu_attach_device(drm_dev, dev); 248 249 exynos_drm_ipp_register(drm_dev, ipp, &ipp_funcs, 250 DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE, 251 rot->formats, rot->num_formats, "rotator"); 252 253 dev_info(dev, "The exynos rotator has been probed successfully\n"); 254 255 return 0; 256 } 257 258 static void rotator_unbind(struct device *dev, struct device *master, 259 void *data) 260 { 261 struct rot_context *rot = dev_get_drvdata(dev); 262 struct drm_device *drm_dev = data; 263 struct exynos_drm_ipp *ipp = &rot->ipp; 264 265 exynos_drm_ipp_unregister(drm_dev, ipp); 266 drm_iommu_detach_device(rot->drm_dev, rot->dev); 267 } 268 269 static const struct component_ops rotator_component_ops = { 270 .bind = rotator_bind, 271 .unbind = rotator_unbind, 272 }; 273 274 static int rotator_probe(struct platform_device *pdev) 275 { 276 struct device *dev = &pdev->dev; 277 struct resource *regs_res; 278 struct rot_context *rot; 279 const struct rot_variant *variant; 280 int irq; 281 int ret; 282 283 rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); 284 if (!rot) 285 return -ENOMEM; 286 287 variant = of_device_get_match_data(dev); 288 rot->formats = variant->formats; 289 rot->num_formats = variant->num_formats; 290 rot->dev = dev; 291 regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 292 rot->regs = devm_ioremap_resource(dev, regs_res); 293 if (IS_ERR(rot->regs)) 294 return PTR_ERR(rot->regs); 295 296 irq = platform_get_irq(pdev, 0); 297 if (irq < 0) { 298 dev_err(dev, "failed to get irq\n"); 299 return irq; 300 } 301 302 ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev), 303 rot); 304 if (ret < 0) { 305 dev_err(dev, "failed to request irq\n"); 306 return ret; 307 } 308 309 rot->clock = devm_clk_get(dev, "rotator"); 310 if (IS_ERR(rot->clock)) { 311 dev_err(dev, "failed to get clock\n"); 312 return PTR_ERR(rot->clock); 313 } 314 315 pm_runtime_use_autosuspend(dev); 316 pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY); 317 pm_runtime_enable(dev); 318 platform_set_drvdata(pdev, rot); 319 320 ret = component_add(dev, &rotator_component_ops); 321 if (ret) 322 goto err_component; 323 324 return 0; 325 326 err_component: 327 pm_runtime_dont_use_autosuspend(dev); 328 pm_runtime_disable(dev); 329 return ret; 330 } 331 332 static int rotator_remove(struct platform_device *pdev) 333 { 334 struct device *dev = &pdev->dev; 335 336 component_del(dev, &rotator_component_ops); 337 pm_runtime_dont_use_autosuspend(dev); 338 pm_runtime_disable(dev); 339 340 return 0; 341 } 342 343 #ifdef CONFIG_PM 344 static int rotator_runtime_suspend(struct device *dev) 345 { 346 struct rot_context *rot = dev_get_drvdata(dev); 347 348 clk_disable_unprepare(rot->clock); 349 return 0; 350 } 351 352 static int rotator_runtime_resume(struct device *dev) 353 { 354 struct rot_context *rot = dev_get_drvdata(dev); 355 356 return clk_prepare_enable(rot->clock); 357 } 358 #endif 359 360 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = { 361 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) }, 362 { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) }, 363 }; 364 365 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = { 366 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) }, 367 { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) }, 368 }; 369 370 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = { 371 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) }, 372 { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) }, 373 }; 374 375 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = { 376 { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) }, 377 { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) }, 378 }; 379 380 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = { 381 { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) }, 382 { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) }, 383 }; 384 385 static const struct exynos_drm_ipp_formats rotator_4210_formats[] = { 386 { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) }, 387 { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) }, 388 }; 389 390 static const struct exynos_drm_ipp_formats rotator_4412_formats[] = { 391 { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) }, 392 { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) }, 393 }; 394 395 static const struct exynos_drm_ipp_formats rotator_5250_formats[] = { 396 { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) }, 397 { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) }, 398 }; 399 400 static const struct rot_variant rotator_4210_data = { 401 .formats = rotator_4210_formats, 402 .num_formats = ARRAY_SIZE(rotator_4210_formats), 403 }; 404 405 static const struct rot_variant rotator_4412_data = { 406 .formats = rotator_4412_formats, 407 .num_formats = ARRAY_SIZE(rotator_4412_formats), 408 }; 409 410 static const struct rot_variant rotator_5250_data = { 411 .formats = rotator_5250_formats, 412 .num_formats = ARRAY_SIZE(rotator_5250_formats), 413 }; 414 415 static const struct of_device_id exynos_rotator_match[] = { 416 { 417 .compatible = "samsung,exynos4210-rotator", 418 .data = &rotator_4210_data, 419 }, { 420 .compatible = "samsung,exynos4212-rotator", 421 .data = &rotator_4412_data, 422 }, { 423 .compatible = "samsung,exynos5250-rotator", 424 .data = &rotator_5250_data, 425 }, { 426 }, 427 }; 428 MODULE_DEVICE_TABLE(of, exynos_rotator_match); 429 430 static const struct dev_pm_ops rotator_pm_ops = { 431 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 432 pm_runtime_force_resume) 433 SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume, 434 NULL) 435 }; 436 437 struct platform_driver rotator_driver = { 438 .probe = rotator_probe, 439 .remove = rotator_remove, 440 .driver = { 441 .name = "exynos-rotator", 442 .owner = THIS_MODULE, 443 .pm = &rotator_pm_ops, 444 .of_match_table = exynos_rotator_match, 445 }, 446 }; 447