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