Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2018 BayLibre, SAS
0004  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
0005  * Copyright (C) 2014 Endless Mobile
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/mfd/syscon.h>
0010 #include <linux/module.h>
0011 #include <linux/regmap.h>
0012 #include <linux/soc/amlogic/meson-canvas.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_platform.h>
0015 #include <linux/io.h>
0016 
0017 #define NUM_CANVAS 256
0018 
0019 /* DMC Registers */
0020 #define DMC_CAV_LUT_DATAL   0x00
0021     #define CANVAS_WIDTH_LBIT   29
0022     #define CANVAS_WIDTH_LWID   3
0023 #define DMC_CAV_LUT_DATAH   0x04
0024     #define CANVAS_WIDTH_HBIT   0
0025     #define CANVAS_HEIGHT_BIT   9
0026     #define CANVAS_WRAP_BIT     22
0027     #define CANVAS_BLKMODE_BIT  24
0028     #define CANVAS_ENDIAN_BIT   26
0029 #define DMC_CAV_LUT_ADDR    0x08
0030     #define CANVAS_LUT_WR_EN    BIT(9)
0031     #define CANVAS_LUT_RD_EN    BIT(8)
0032 
0033 struct meson_canvas {
0034     struct device *dev;
0035     void __iomem *reg_base;
0036     spinlock_t lock; /* canvas device lock */
0037     u8 used[NUM_CANVAS];
0038     bool supports_endianness;
0039 };
0040 
0041 static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
0042 {
0043     writel_relaxed(val, canvas->reg_base + reg);
0044 }
0045 
0046 static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
0047 {
0048     return readl_relaxed(canvas->reg_base + reg);
0049 }
0050 
0051 struct meson_canvas *meson_canvas_get(struct device *dev)
0052 {
0053     struct device_node *canvas_node;
0054     struct platform_device *canvas_pdev;
0055     struct meson_canvas *canvas;
0056 
0057     canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
0058     if (!canvas_node)
0059         return ERR_PTR(-ENODEV);
0060 
0061     canvas_pdev = of_find_device_by_node(canvas_node);
0062     if (!canvas_pdev) {
0063         of_node_put(canvas_node);
0064         return ERR_PTR(-EPROBE_DEFER);
0065     }
0066 
0067     of_node_put(canvas_node);
0068 
0069     /*
0070      * If priv is NULL, it's probably because the canvas hasn't
0071      * properly initialized. Bail out with -EINVAL because, in the
0072      * current state, this driver probe cannot return -EPROBE_DEFER
0073      */
0074     canvas = dev_get_drvdata(&canvas_pdev->dev);
0075     if (!canvas) {
0076         put_device(&canvas_pdev->dev);
0077         return ERR_PTR(-EINVAL);
0078     }
0079 
0080     return canvas;
0081 }
0082 EXPORT_SYMBOL_GPL(meson_canvas_get);
0083 
0084 int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
0085             u32 addr, u32 stride, u32 height,
0086             unsigned int wrap,
0087             unsigned int blkmode,
0088             unsigned int endian)
0089 {
0090     unsigned long flags;
0091 
0092     if (endian && !canvas->supports_endianness) {
0093         dev_err(canvas->dev,
0094             "Endianness is not supported on this SoC\n");
0095         return -EINVAL;
0096     }
0097 
0098     spin_lock_irqsave(&canvas->lock, flags);
0099     if (!canvas->used[canvas_index]) {
0100         dev_err(canvas->dev,
0101             "Trying to setup non allocated canvas %u\n",
0102             canvas_index);
0103         spin_unlock_irqrestore(&canvas->lock, flags);
0104         return -EINVAL;
0105     }
0106 
0107     canvas_write(canvas, DMC_CAV_LUT_DATAL,
0108              ((addr + 7) >> 3) |
0109              (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
0110 
0111     canvas_write(canvas, DMC_CAV_LUT_DATAH,
0112              ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
0113                         CANVAS_WIDTH_HBIT) |
0114              (height << CANVAS_HEIGHT_BIT) |
0115              (wrap << CANVAS_WRAP_BIT) |
0116              (blkmode << CANVAS_BLKMODE_BIT) |
0117              (endian << CANVAS_ENDIAN_BIT));
0118 
0119     canvas_write(canvas, DMC_CAV_LUT_ADDR,
0120              CANVAS_LUT_WR_EN | canvas_index);
0121 
0122     /* Force a read-back to make sure everything is flushed. */
0123     canvas_read(canvas, DMC_CAV_LUT_DATAH);
0124     spin_unlock_irqrestore(&canvas->lock, flags);
0125 
0126     return 0;
0127 }
0128 EXPORT_SYMBOL_GPL(meson_canvas_config);
0129 
0130 int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
0131 {
0132     int i;
0133     unsigned long flags;
0134 
0135     spin_lock_irqsave(&canvas->lock, flags);
0136     for (i = 0; i < NUM_CANVAS; ++i) {
0137         if (!canvas->used[i]) {
0138             canvas->used[i] = 1;
0139             spin_unlock_irqrestore(&canvas->lock, flags);
0140             *canvas_index = i;
0141             return 0;
0142         }
0143     }
0144     spin_unlock_irqrestore(&canvas->lock, flags);
0145 
0146     dev_err(canvas->dev, "No more canvas available\n");
0147     return -ENODEV;
0148 }
0149 EXPORT_SYMBOL_GPL(meson_canvas_alloc);
0150 
0151 int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
0152 {
0153     unsigned long flags;
0154 
0155     spin_lock_irqsave(&canvas->lock, flags);
0156     if (!canvas->used[canvas_index]) {
0157         dev_err(canvas->dev,
0158             "Trying to free unused canvas %u\n", canvas_index);
0159         spin_unlock_irqrestore(&canvas->lock, flags);
0160         return -EINVAL;
0161     }
0162     canvas->used[canvas_index] = 0;
0163     spin_unlock_irqrestore(&canvas->lock, flags);
0164 
0165     return 0;
0166 }
0167 EXPORT_SYMBOL_GPL(meson_canvas_free);
0168 
0169 static int meson_canvas_probe(struct platform_device *pdev)
0170 {
0171     struct meson_canvas *canvas;
0172     struct device *dev = &pdev->dev;
0173 
0174     canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
0175     if (!canvas)
0176         return -ENOMEM;
0177 
0178     canvas->reg_base = devm_platform_ioremap_resource(pdev, 0);
0179     if (IS_ERR(canvas->reg_base))
0180         return PTR_ERR(canvas->reg_base);
0181 
0182     canvas->supports_endianness = of_device_get_match_data(dev);
0183 
0184     canvas->dev = dev;
0185     spin_lock_init(&canvas->lock);
0186     dev_set_drvdata(dev, canvas);
0187 
0188     return 0;
0189 }
0190 
0191 static const struct of_device_id canvas_dt_match[] = {
0192     { .compatible = "amlogic,meson8-canvas", .data = (void *)false, },
0193     { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, },
0194     { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, },
0195     { .compatible = "amlogic,canvas", .data = (void *)true, },
0196     {}
0197 };
0198 MODULE_DEVICE_TABLE(of, canvas_dt_match);
0199 
0200 static struct platform_driver meson_canvas_driver = {
0201     .probe = meson_canvas_probe,
0202     .driver = {
0203         .name = "amlogic-canvas",
0204         .of_match_table = canvas_dt_match,
0205     },
0206 };
0207 module_platform_driver(meson_canvas_driver);
0208 
0209 MODULE_DESCRIPTION("Amlogic Canvas driver");
0210 MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>");
0211 MODULE_LICENSE("GPL");