Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 Samsung Electronics Co.Ltd
0004  * Authors:
0005  *  YoungJun Cho <yj44.cho@samsung.com>
0006  *  Eunchul Kim <chulspro.kim@samsung.com>
0007  */
0008 
0009 #include <linux/clk.h>
0010 #include <linux/component.h>
0011 #include <linux/err.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/io.h>
0014 #include <linux/kernel.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/pm_runtime.h>
0018 #include <linux/sizes.h>
0019 
0020 #include <drm/drm_fourcc.h>
0021 #include <drm/exynos_drm.h>
0022 
0023 #include "exynos_drm_drv.h"
0024 #include "exynos_drm_ipp.h"
0025 #include "regs-rotator.h"
0026 
0027 /*
0028  * Rotator supports image crop/rotator and input/output DMA operations.
0029  * input DMA reads image data from the memory.
0030  * output DMA writes image data to memory.
0031  */
0032 
0033 #define ROTATOR_AUTOSUSPEND_DELAY   2000
0034 
0035 #define rot_read(offset)    readl(rot->regs + (offset))
0036 #define rot_write(cfg, offset)  writel(cfg, rot->regs + (offset))
0037 
0038 enum rot_irq_status {
0039     ROT_IRQ_STATUS_COMPLETE = 8,
0040     ROT_IRQ_STATUS_ILLEGAL  = 9,
0041 };
0042 
0043 struct rot_variant {
0044     const struct exynos_drm_ipp_formats *formats;
0045     unsigned int    num_formats;
0046 };
0047 
0048 /*
0049  * A structure of rotator context.
0050  * @ippdrv: prepare initialization using ippdrv.
0051  * @regs: memory mapped io registers.
0052  * @clock: rotator gate clock.
0053  * @limit_tbl: limitation of rotator.
0054  * @irq: irq number.
0055  */
0056 struct rot_context {
0057     struct exynos_drm_ipp ipp;
0058     struct drm_device *drm_dev;
0059     void        *dma_priv;
0060     struct device   *dev;
0061     void __iomem    *regs;
0062     struct clk  *clock;
0063     const struct exynos_drm_ipp_formats *formats;
0064     unsigned int    num_formats;
0065     struct exynos_drm_ipp_task  *task;
0066 };
0067 
0068 static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
0069 {
0070     u32 val = rot_read(ROT_CONFIG);
0071 
0072     if (enable == true)
0073         val |= ROT_CONFIG_IRQ;
0074     else
0075         val &= ~ROT_CONFIG_IRQ;
0076 
0077     rot_write(val, ROT_CONFIG);
0078 }
0079 
0080 static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
0081 {
0082     u32 val = rot_read(ROT_STATUS);
0083 
0084     val = ROT_STATUS_IRQ(val);
0085 
0086     if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
0087         return ROT_IRQ_STATUS_COMPLETE;
0088 
0089     return ROT_IRQ_STATUS_ILLEGAL;
0090 }
0091 
0092 static irqreturn_t rotator_irq_handler(int irq, void *arg)
0093 {
0094     struct rot_context *rot = arg;
0095     enum rot_irq_status irq_status;
0096     u32 val;
0097 
0098     /* Get execution result */
0099     irq_status = rotator_reg_get_irq_status(rot);
0100 
0101     /* clear status */
0102     val = rot_read(ROT_STATUS);
0103     val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
0104     rot_write(val, ROT_STATUS);
0105 
0106     if (rot->task) {
0107         struct exynos_drm_ipp_task *task = rot->task;
0108 
0109         rot->task = NULL;
0110         pm_runtime_mark_last_busy(rot->dev);
0111         pm_runtime_put_autosuspend(rot->dev);
0112         exynos_drm_ipp_task_done(task,
0113             irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL);
0114     }
0115 
0116     return IRQ_HANDLED;
0117 }
0118 
0119 static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt)
0120 {
0121     u32 val;
0122 
0123     val = rot_read(ROT_CONTROL);
0124     val &= ~ROT_CONTROL_FMT_MASK;
0125 
0126     switch (fmt) {
0127     case DRM_FORMAT_NV12:
0128         val |= ROT_CONTROL_FMT_YCBCR420_2P;
0129         break;
0130     case DRM_FORMAT_XRGB8888:
0131         val |= ROT_CONTROL_FMT_RGB888;
0132         break;
0133     }
0134 
0135     rot_write(val, ROT_CONTROL);
0136 }
0137 
0138 static void rotator_src_set_buf(struct rot_context *rot,
0139                 struct exynos_drm_ipp_buffer *buf)
0140 {
0141     u32 val;
0142 
0143     /* Set buffer size configuration */
0144     val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
0145           ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
0146     rot_write(val, ROT_SRC_BUF_SIZE);
0147 
0148     /* Set crop image position configuration */
0149     val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
0150     rot_write(val, ROT_SRC_CROP_POS);
0151     val = ROT_SRC_CROP_SIZE_H(buf->rect.h) |
0152           ROT_SRC_CROP_SIZE_W(buf->rect.w);
0153     rot_write(val, ROT_SRC_CROP_SIZE);
0154 
0155     /* Set buffer DMA address */
0156     rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0));
0157     rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1));
0158 }
0159 
0160 static void rotator_dst_set_transf(struct rot_context *rot,
0161                    unsigned int rotation)
0162 {
0163     u32 val;
0164 
0165     /* Set transform configuration */
0166     val = rot_read(ROT_CONTROL);
0167     val &= ~ROT_CONTROL_FLIP_MASK;
0168 
0169     if (rotation & DRM_MODE_REFLECT_X)
0170         val |= ROT_CONTROL_FLIP_VERTICAL;
0171     if (rotation & DRM_MODE_REFLECT_Y)
0172         val |= ROT_CONTROL_FLIP_HORIZONTAL;
0173 
0174     val &= ~ROT_CONTROL_ROT_MASK;
0175 
0176     if (rotation & DRM_MODE_ROTATE_90)
0177         val |= ROT_CONTROL_ROT_90;
0178     else if (rotation & DRM_MODE_ROTATE_180)
0179         val |= ROT_CONTROL_ROT_180;
0180     else if (rotation & DRM_MODE_ROTATE_270)
0181         val |= ROT_CONTROL_ROT_270;
0182 
0183     rot_write(val, ROT_CONTROL);
0184 }
0185 
0186 static void rotator_dst_set_buf(struct rot_context *rot,
0187                 struct exynos_drm_ipp_buffer *buf)
0188 {
0189     u32 val;
0190 
0191     /* Set buffer size configuration */
0192     val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
0193           ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
0194     rot_write(val, ROT_DST_BUF_SIZE);
0195 
0196     /* Set crop image position configuration */
0197     val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
0198     rot_write(val, ROT_DST_CROP_POS);
0199 
0200     /* Set buffer DMA address */
0201     rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0));
0202     rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1));
0203 }
0204 
0205 static void rotator_start(struct rot_context *rot)
0206 {
0207     u32 val;
0208 
0209     /* Set interrupt enable */
0210     rotator_reg_set_irq(rot, true);
0211 
0212     val = rot_read(ROT_CONTROL);
0213     val |= ROT_CONTROL_START;
0214     rot_write(val, ROT_CONTROL);
0215 }
0216 
0217 static int rotator_commit(struct exynos_drm_ipp *ipp,
0218               struct exynos_drm_ipp_task *task)
0219 {
0220     struct rot_context *rot =
0221             container_of(ipp, struct rot_context, ipp);
0222     int ret;
0223 
0224     ret = pm_runtime_resume_and_get(rot->dev);
0225     if (ret < 0) {
0226         dev_err(rot->dev, "failed to enable ROTATOR device.\n");
0227         return ret;
0228     }
0229     rot->task = task;
0230 
0231     rotator_src_set_fmt(rot, task->src.buf.fourcc);
0232     rotator_src_set_buf(rot, &task->src);
0233     rotator_dst_set_transf(rot, task->transform.rotation);
0234     rotator_dst_set_buf(rot, &task->dst);
0235     rotator_start(rot);
0236 
0237     return 0;
0238 }
0239 
0240 static const struct exynos_drm_ipp_funcs ipp_funcs = {
0241     .commit = rotator_commit,
0242 };
0243 
0244 static int rotator_bind(struct device *dev, struct device *master, void *data)
0245 {
0246     struct rot_context *rot = dev_get_drvdata(dev);
0247     struct drm_device *drm_dev = data;
0248     struct exynos_drm_ipp *ipp = &rot->ipp;
0249 
0250     rot->drm_dev = drm_dev;
0251     ipp->drm_dev = drm_dev;
0252     exynos_drm_register_dma(drm_dev, dev, &rot->dma_priv);
0253 
0254     exynos_drm_ipp_register(dev, ipp, &ipp_funcs,
0255                DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE,
0256                rot->formats, rot->num_formats, "rotator");
0257 
0258     dev_info(dev, "The exynos rotator has been probed successfully\n");
0259 
0260     return 0;
0261 }
0262 
0263 static void rotator_unbind(struct device *dev, struct device *master,
0264             void *data)
0265 {
0266     struct rot_context *rot = dev_get_drvdata(dev);
0267     struct exynos_drm_ipp *ipp = &rot->ipp;
0268 
0269     exynos_drm_ipp_unregister(dev, ipp);
0270     exynos_drm_unregister_dma(rot->drm_dev, rot->dev, &rot->dma_priv);
0271 }
0272 
0273 static const struct component_ops rotator_component_ops = {
0274     .bind   = rotator_bind,
0275     .unbind = rotator_unbind,
0276 };
0277 
0278 static int rotator_probe(struct platform_device *pdev)
0279 {
0280     struct device *dev = &pdev->dev;
0281     struct rot_context *rot;
0282     const struct rot_variant *variant;
0283     int irq;
0284     int ret;
0285 
0286     rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
0287     if (!rot)
0288         return -ENOMEM;
0289 
0290     variant = of_device_get_match_data(dev);
0291     rot->formats = variant->formats;
0292     rot->num_formats = variant->num_formats;
0293     rot->dev = dev;
0294     rot->regs = devm_platform_ioremap_resource(pdev, 0);
0295     if (IS_ERR(rot->regs))
0296         return PTR_ERR(rot->regs);
0297 
0298     irq = platform_get_irq(pdev, 0);
0299     if (irq < 0)
0300         return irq;
0301 
0302     ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev),
0303                    rot);
0304     if (ret < 0) {
0305         dev_err(dev, "failed to request irq\n");
0306         return ret;
0307     }
0308 
0309     rot->clock = devm_clk_get(dev, "rotator");
0310     if (IS_ERR(rot->clock)) {
0311         dev_err(dev, "failed to get clock\n");
0312         return PTR_ERR(rot->clock);
0313     }
0314 
0315     pm_runtime_use_autosuspend(dev);
0316     pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY);
0317     pm_runtime_enable(dev);
0318     platform_set_drvdata(pdev, rot);
0319 
0320     ret = component_add(dev, &rotator_component_ops);
0321     if (ret)
0322         goto err_component;
0323 
0324     return 0;
0325 
0326 err_component:
0327     pm_runtime_dont_use_autosuspend(dev);
0328     pm_runtime_disable(dev);
0329     return ret;
0330 }
0331 
0332 static int rotator_remove(struct platform_device *pdev)
0333 {
0334     struct device *dev = &pdev->dev;
0335 
0336     component_del(dev, &rotator_component_ops);
0337     pm_runtime_dont_use_autosuspend(dev);
0338     pm_runtime_disable(dev);
0339 
0340     return 0;
0341 }
0342 
0343 #ifdef CONFIG_PM
0344 static int rotator_runtime_suspend(struct device *dev)
0345 {
0346     struct rot_context *rot = dev_get_drvdata(dev);
0347 
0348     clk_disable_unprepare(rot->clock);
0349     return 0;
0350 }
0351 
0352 static int rotator_runtime_resume(struct device *dev)
0353 {
0354     struct rot_context *rot = dev_get_drvdata(dev);
0355 
0356     return clk_prepare_enable(rot->clock);
0357 }
0358 #endif
0359 
0360 static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = {
0361     { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
0362     { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
0363 };
0364 
0365 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = {
0366     { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
0367     { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
0368 };
0369 
0370 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = {
0371     { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
0372     { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
0373 };
0374 
0375 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = {
0376     { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
0377     { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
0378 };
0379 
0380 static const struct drm_exynos_ipp_limit rotator_s5pv210_yuv_limits[] = {
0381     { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
0382     { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
0383 };
0384 
0385 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = {
0386     { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
0387     { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
0388 };
0389 
0390 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = {
0391     { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) },
0392     { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
0393 };
0394 
0395 static const struct exynos_drm_ipp_formats rotator_s5pv210_formats[] = {
0396     { IPP_SRCDST_FORMAT(XRGB8888, rotator_s5pv210_rbg888_limits) },
0397     { IPP_SRCDST_FORMAT(NV12, rotator_s5pv210_yuv_limits) },
0398 };
0399 
0400 static const struct exynos_drm_ipp_formats rotator_4210_formats[] = {
0401     { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) },
0402     { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) },
0403 };
0404 
0405 static const struct exynos_drm_ipp_formats rotator_4412_formats[] = {
0406     { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) },
0407     { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
0408 };
0409 
0410 static const struct exynos_drm_ipp_formats rotator_5250_formats[] = {
0411     { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) },
0412     { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
0413 };
0414 
0415 static const struct rot_variant rotator_s5pv210_data = {
0416     .formats = rotator_s5pv210_formats,
0417     .num_formats = ARRAY_SIZE(rotator_s5pv210_formats),
0418 };
0419 
0420 static const struct rot_variant rotator_4210_data = {
0421     .formats = rotator_4210_formats,
0422     .num_formats = ARRAY_SIZE(rotator_4210_formats),
0423 };
0424 
0425 static const struct rot_variant rotator_4412_data = {
0426     .formats = rotator_4412_formats,
0427     .num_formats = ARRAY_SIZE(rotator_4412_formats),
0428 };
0429 
0430 static const struct rot_variant rotator_5250_data = {
0431     .formats = rotator_5250_formats,
0432     .num_formats = ARRAY_SIZE(rotator_5250_formats),
0433 };
0434 
0435 static const struct of_device_id exynos_rotator_match[] = {
0436     {
0437         .compatible = "samsung,s5pv210-rotator",
0438         .data = &rotator_s5pv210_data,
0439     }, {
0440         .compatible = "samsung,exynos4210-rotator",
0441         .data = &rotator_4210_data,
0442     }, {
0443         .compatible = "samsung,exynos4212-rotator",
0444         .data = &rotator_4412_data,
0445     }, {
0446         .compatible = "samsung,exynos5250-rotator",
0447         .data = &rotator_5250_data,
0448     }, {
0449     },
0450 };
0451 MODULE_DEVICE_TABLE(of, exynos_rotator_match);
0452 
0453 static const struct dev_pm_ops rotator_pm_ops = {
0454     SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0455                 pm_runtime_force_resume)
0456     SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
0457                                     NULL)
0458 };
0459 
0460 struct platform_driver rotator_driver = {
0461     .probe      = rotator_probe,
0462     .remove     = rotator_remove,
0463     .driver     = {
0464         .name   = "exynos-rotator",
0465         .owner  = THIS_MODULE,
0466         .pm = &rotator_pm_ops,
0467         .of_match_table = exynos_rotator_match,
0468     },
0469 };