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