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