1d4983983SMaxime Jourdan // SPDX-License-Identifier: GPL-2.0+ 2d4983983SMaxime Jourdan /* 3d4983983SMaxime Jourdan * Copyright (C) 2018 BayLibre, SAS 4d4983983SMaxime Jourdan * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 5d4983983SMaxime Jourdan * Copyright (C) 2014 Endless Mobile 6d4983983SMaxime Jourdan */ 7d4983983SMaxime Jourdan 8d4983983SMaxime Jourdan #include <linux/kernel.h> 9d4983983SMaxime Jourdan #include <linux/mfd/syscon.h> 10d4983983SMaxime Jourdan #include <linux/module.h> 11d4983983SMaxime Jourdan #include <linux/regmap.h> 12d4983983SMaxime Jourdan #include <linux/soc/amlogic/meson-canvas.h> 13d4983983SMaxime Jourdan #include <linux/of_address.h> 14d4983983SMaxime Jourdan #include <linux/of_platform.h> 15d4983983SMaxime Jourdan #include <linux/io.h> 16d4983983SMaxime Jourdan 17d4983983SMaxime Jourdan #define NUM_CANVAS 256 18d4983983SMaxime Jourdan 19d4983983SMaxime Jourdan /* DMC Registers */ 20d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAL 0x00 21d4983983SMaxime Jourdan #define CANVAS_WIDTH_LBIT 29 22d4983983SMaxime Jourdan #define CANVAS_WIDTH_LWID 3 23d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAH 0x04 24d4983983SMaxime Jourdan #define CANVAS_WIDTH_HBIT 0 25d4983983SMaxime Jourdan #define CANVAS_HEIGHT_BIT 9 26d4983983SMaxime Jourdan #define CANVAS_WRAP_BIT 22 27d4983983SMaxime Jourdan #define CANVAS_BLKMODE_BIT 24 28d4983983SMaxime Jourdan #define CANVAS_ENDIAN_BIT 26 29d4983983SMaxime Jourdan #define DMC_CAV_LUT_ADDR 0x08 30d4983983SMaxime Jourdan #define CANVAS_LUT_WR_EN BIT(9) 31d4983983SMaxime Jourdan #define CANVAS_LUT_RD_EN BIT(8) 32d4983983SMaxime Jourdan 33d4983983SMaxime Jourdan struct meson_canvas { 34d4983983SMaxime Jourdan struct device *dev; 35d4983983SMaxime Jourdan void __iomem *reg_base; 36d4983983SMaxime Jourdan spinlock_t lock; /* canvas device lock */ 37d4983983SMaxime Jourdan u8 used[NUM_CANVAS]; 389a98fdf5SMartin Blumenstingl bool supports_endianness; 39d4983983SMaxime Jourdan }; 40d4983983SMaxime Jourdan 41d4983983SMaxime Jourdan static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val) 42d4983983SMaxime Jourdan { 43d4983983SMaxime Jourdan writel_relaxed(val, canvas->reg_base + reg); 44d4983983SMaxime Jourdan } 45d4983983SMaxime Jourdan 46d4983983SMaxime Jourdan static u32 canvas_read(struct meson_canvas *canvas, u32 reg) 47d4983983SMaxime Jourdan { 48d4983983SMaxime Jourdan return readl_relaxed(canvas->reg_base + reg); 49d4983983SMaxime Jourdan } 50d4983983SMaxime Jourdan 51d4983983SMaxime Jourdan struct meson_canvas *meson_canvas_get(struct device *dev) 52d4983983SMaxime Jourdan { 53d4983983SMaxime Jourdan struct device_node *canvas_node; 54d4983983SMaxime Jourdan struct platform_device *canvas_pdev; 55382f8be0SNeil Armstrong struct meson_canvas *canvas; 56d4983983SMaxime Jourdan 57d4983983SMaxime Jourdan canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); 58d4983983SMaxime Jourdan if (!canvas_node) 59d4983983SMaxime Jourdan return ERR_PTR(-ENODEV); 60d4983983SMaxime Jourdan 61d4983983SMaxime Jourdan canvas_pdev = of_find_device_by_node(canvas_node); 6299e5a8dfSwen yang if (!canvas_pdev) { 6399e5a8dfSwen yang of_node_put(canvas_node); 64d4983983SMaxime Jourdan return ERR_PTR(-EPROBE_DEFER); 6599e5a8dfSwen yang } 66d4983983SMaxime Jourdan 6799e5a8dfSwen yang of_node_put(canvas_node); 68382f8be0SNeil Armstrong 69382f8be0SNeil Armstrong /* 70382f8be0SNeil Armstrong * If priv is NULL, it's probably because the canvas hasn't 71382f8be0SNeil Armstrong * properly initialized. Bail out with -EINVAL because, in the 72382f8be0SNeil Armstrong * current state, this driver probe cannot return -EPROBE_DEFER 73382f8be0SNeil Armstrong */ 74382f8be0SNeil Armstrong canvas = dev_get_drvdata(&canvas_pdev->dev); 75*28f851e6SYu Kuai if (!canvas) { 76*28f851e6SYu Kuai put_device(&canvas_pdev->dev); 77382f8be0SNeil Armstrong return ERR_PTR(-EINVAL); 78*28f851e6SYu Kuai } 79382f8be0SNeil Armstrong 80382f8be0SNeil Armstrong return canvas; 81d4983983SMaxime Jourdan } 82d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get); 83d4983983SMaxime Jourdan 84d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, 85d4983983SMaxime Jourdan u32 addr, u32 stride, u32 height, 86d4983983SMaxime Jourdan unsigned int wrap, 87d4983983SMaxime Jourdan unsigned int blkmode, 88d4983983SMaxime Jourdan unsigned int endian) 89d4983983SMaxime Jourdan { 90d4983983SMaxime Jourdan unsigned long flags; 91d4983983SMaxime Jourdan 929a98fdf5SMartin Blumenstingl if (endian && !canvas->supports_endianness) { 939a98fdf5SMartin Blumenstingl dev_err(canvas->dev, 949a98fdf5SMartin Blumenstingl "Endianness is not supported on this SoC\n"); 959a98fdf5SMartin Blumenstingl return -EINVAL; 969a98fdf5SMartin Blumenstingl } 979a98fdf5SMartin Blumenstingl 98d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 99d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 100d4983983SMaxime Jourdan dev_err(canvas->dev, 101d4983983SMaxime Jourdan "Trying to setup non allocated canvas %u\n", 102d4983983SMaxime Jourdan canvas_index); 103d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 104d4983983SMaxime Jourdan return -EINVAL; 105d4983983SMaxime Jourdan } 106d4983983SMaxime Jourdan 107d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAL, 108d4983983SMaxime Jourdan ((addr + 7) >> 3) | 109d4983983SMaxime Jourdan (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); 110d4983983SMaxime Jourdan 111d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAH, 112d4983983SMaxime Jourdan ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << 113d4983983SMaxime Jourdan CANVAS_WIDTH_HBIT) | 114d4983983SMaxime Jourdan (height << CANVAS_HEIGHT_BIT) | 115d4983983SMaxime Jourdan (wrap << CANVAS_WRAP_BIT) | 116d4983983SMaxime Jourdan (blkmode << CANVAS_BLKMODE_BIT) | 117d4983983SMaxime Jourdan (endian << CANVAS_ENDIAN_BIT)); 118d4983983SMaxime Jourdan 119d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_ADDR, 120d4983983SMaxime Jourdan CANVAS_LUT_WR_EN | canvas_index); 121d4983983SMaxime Jourdan 122d4983983SMaxime Jourdan /* Force a read-back to make sure everything is flushed. */ 123d4983983SMaxime Jourdan canvas_read(canvas, DMC_CAV_LUT_DATAH); 124d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 125d4983983SMaxime Jourdan 126d4983983SMaxime Jourdan return 0; 127d4983983SMaxime Jourdan } 128d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config); 129d4983983SMaxime Jourdan 130d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index) 131d4983983SMaxime Jourdan { 132d4983983SMaxime Jourdan int i; 133d4983983SMaxime Jourdan unsigned long flags; 134d4983983SMaxime Jourdan 135d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 136d4983983SMaxime Jourdan for (i = 0; i < NUM_CANVAS; ++i) { 137d4983983SMaxime Jourdan if (!canvas->used[i]) { 138d4983983SMaxime Jourdan canvas->used[i] = 1; 139d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 140d4983983SMaxime Jourdan *canvas_index = i; 141d4983983SMaxime Jourdan return 0; 142d4983983SMaxime Jourdan } 143d4983983SMaxime Jourdan } 144d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 145d4983983SMaxime Jourdan 146d4983983SMaxime Jourdan dev_err(canvas->dev, "No more canvas available\n"); 147d4983983SMaxime Jourdan return -ENODEV; 148d4983983SMaxime Jourdan } 149d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc); 150d4983983SMaxime Jourdan 151d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index) 152d4983983SMaxime Jourdan { 153d4983983SMaxime Jourdan unsigned long flags; 154d4983983SMaxime Jourdan 155d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 156d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 157d4983983SMaxime Jourdan dev_err(canvas->dev, 158d4983983SMaxime Jourdan "Trying to free unused canvas %u\n", canvas_index); 159d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 160d4983983SMaxime Jourdan return -EINVAL; 161d4983983SMaxime Jourdan } 162d4983983SMaxime Jourdan canvas->used[canvas_index] = 0; 163d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 164d4983983SMaxime Jourdan 165d4983983SMaxime Jourdan return 0; 166d4983983SMaxime Jourdan } 167d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free); 168d4983983SMaxime Jourdan 169d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev) 170d4983983SMaxime Jourdan { 171d4983983SMaxime Jourdan struct resource *res; 172d4983983SMaxime Jourdan struct meson_canvas *canvas; 173d4983983SMaxime Jourdan struct device *dev = &pdev->dev; 174d4983983SMaxime Jourdan 175d4983983SMaxime Jourdan canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL); 176d4983983SMaxime Jourdan if (!canvas) 177d4983983SMaxime Jourdan return -ENOMEM; 178d4983983SMaxime Jourdan 179d4983983SMaxime Jourdan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 180d4983983SMaxime Jourdan canvas->reg_base = devm_ioremap_resource(dev, res); 181d4983983SMaxime Jourdan if (IS_ERR(canvas->reg_base)) 182d4983983SMaxime Jourdan return PTR_ERR(canvas->reg_base); 183d4983983SMaxime Jourdan 1849a98fdf5SMartin Blumenstingl canvas->supports_endianness = of_device_get_match_data(dev); 1859a98fdf5SMartin Blumenstingl 186d4983983SMaxime Jourdan canvas->dev = dev; 187d4983983SMaxime Jourdan spin_lock_init(&canvas->lock); 188d4983983SMaxime Jourdan dev_set_drvdata(dev, canvas); 189d4983983SMaxime Jourdan 190d4983983SMaxime Jourdan return 0; 191d4983983SMaxime Jourdan } 192d4983983SMaxime Jourdan 193d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = { 1949a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8-canvas", .data = (void *)false, }, 1959a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, }, 1969a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, }, 1979a98fdf5SMartin Blumenstingl { .compatible = "amlogic,canvas", .data = (void *)true, }, 198d4983983SMaxime Jourdan {} 199d4983983SMaxime Jourdan }; 200d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match); 201d4983983SMaxime Jourdan 202d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = { 203d4983983SMaxime Jourdan .probe = meson_canvas_probe, 204d4983983SMaxime Jourdan .driver = { 205d4983983SMaxime Jourdan .name = "amlogic-canvas", 206d4983983SMaxime Jourdan .of_match_table = canvas_dt_match, 207d4983983SMaxime Jourdan }, 208d4983983SMaxime Jourdan }; 209d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver); 210d4983983SMaxime Jourdan 211d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver"); 212d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); 213d4983983SMaxime Jourdan MODULE_LICENSE("GPL"); 214