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]; 38*9a98fdf5SMartin 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); 75382f8be0SNeil Armstrong if (!canvas) 76382f8be0SNeil Armstrong return ERR_PTR(-EINVAL); 77382f8be0SNeil Armstrong 78382f8be0SNeil Armstrong return canvas; 79d4983983SMaxime Jourdan } 80d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get); 81d4983983SMaxime Jourdan 82d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, 83d4983983SMaxime Jourdan u32 addr, u32 stride, u32 height, 84d4983983SMaxime Jourdan unsigned int wrap, 85d4983983SMaxime Jourdan unsigned int blkmode, 86d4983983SMaxime Jourdan unsigned int endian) 87d4983983SMaxime Jourdan { 88d4983983SMaxime Jourdan unsigned long flags; 89d4983983SMaxime Jourdan 90*9a98fdf5SMartin Blumenstingl if (endian && !canvas->supports_endianness) { 91*9a98fdf5SMartin Blumenstingl dev_err(canvas->dev, 92*9a98fdf5SMartin Blumenstingl "Endianness is not supported on this SoC\n"); 93*9a98fdf5SMartin Blumenstingl return -EINVAL; 94*9a98fdf5SMartin Blumenstingl } 95*9a98fdf5SMartin Blumenstingl 96d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 97d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 98d4983983SMaxime Jourdan dev_err(canvas->dev, 99d4983983SMaxime Jourdan "Trying to setup non allocated canvas %u\n", 100d4983983SMaxime Jourdan canvas_index); 101d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 102d4983983SMaxime Jourdan return -EINVAL; 103d4983983SMaxime Jourdan } 104d4983983SMaxime Jourdan 105d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAL, 106d4983983SMaxime Jourdan ((addr + 7) >> 3) | 107d4983983SMaxime Jourdan (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); 108d4983983SMaxime Jourdan 109d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAH, 110d4983983SMaxime Jourdan ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << 111d4983983SMaxime Jourdan CANVAS_WIDTH_HBIT) | 112d4983983SMaxime Jourdan (height << CANVAS_HEIGHT_BIT) | 113d4983983SMaxime Jourdan (wrap << CANVAS_WRAP_BIT) | 114d4983983SMaxime Jourdan (blkmode << CANVAS_BLKMODE_BIT) | 115d4983983SMaxime Jourdan (endian << CANVAS_ENDIAN_BIT)); 116d4983983SMaxime Jourdan 117d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_ADDR, 118d4983983SMaxime Jourdan CANVAS_LUT_WR_EN | canvas_index); 119d4983983SMaxime Jourdan 120d4983983SMaxime Jourdan /* Force a read-back to make sure everything is flushed. */ 121d4983983SMaxime Jourdan canvas_read(canvas, DMC_CAV_LUT_DATAH); 122d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 123d4983983SMaxime Jourdan 124d4983983SMaxime Jourdan return 0; 125d4983983SMaxime Jourdan } 126d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config); 127d4983983SMaxime Jourdan 128d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index) 129d4983983SMaxime Jourdan { 130d4983983SMaxime Jourdan int i; 131d4983983SMaxime Jourdan unsigned long flags; 132d4983983SMaxime Jourdan 133d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 134d4983983SMaxime Jourdan for (i = 0; i < NUM_CANVAS; ++i) { 135d4983983SMaxime Jourdan if (!canvas->used[i]) { 136d4983983SMaxime Jourdan canvas->used[i] = 1; 137d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 138d4983983SMaxime Jourdan *canvas_index = i; 139d4983983SMaxime Jourdan return 0; 140d4983983SMaxime Jourdan } 141d4983983SMaxime Jourdan } 142d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 143d4983983SMaxime Jourdan 144d4983983SMaxime Jourdan dev_err(canvas->dev, "No more canvas available\n"); 145d4983983SMaxime Jourdan return -ENODEV; 146d4983983SMaxime Jourdan } 147d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc); 148d4983983SMaxime Jourdan 149d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index) 150d4983983SMaxime Jourdan { 151d4983983SMaxime Jourdan unsigned long flags; 152d4983983SMaxime Jourdan 153d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 154d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 155d4983983SMaxime Jourdan dev_err(canvas->dev, 156d4983983SMaxime Jourdan "Trying to free unused canvas %u\n", canvas_index); 157d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 158d4983983SMaxime Jourdan return -EINVAL; 159d4983983SMaxime Jourdan } 160d4983983SMaxime Jourdan canvas->used[canvas_index] = 0; 161d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 162d4983983SMaxime Jourdan 163d4983983SMaxime Jourdan return 0; 164d4983983SMaxime Jourdan } 165d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free); 166d4983983SMaxime Jourdan 167d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev) 168d4983983SMaxime Jourdan { 169d4983983SMaxime Jourdan struct resource *res; 170d4983983SMaxime Jourdan struct meson_canvas *canvas; 171d4983983SMaxime Jourdan struct device *dev = &pdev->dev; 172d4983983SMaxime Jourdan 173d4983983SMaxime Jourdan canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL); 174d4983983SMaxime Jourdan if (!canvas) 175d4983983SMaxime Jourdan return -ENOMEM; 176d4983983SMaxime Jourdan 177d4983983SMaxime Jourdan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 178d4983983SMaxime Jourdan canvas->reg_base = devm_ioremap_resource(dev, res); 179d4983983SMaxime Jourdan if (IS_ERR(canvas->reg_base)) 180d4983983SMaxime Jourdan return PTR_ERR(canvas->reg_base); 181d4983983SMaxime Jourdan 182*9a98fdf5SMartin Blumenstingl canvas->supports_endianness = of_device_get_match_data(dev); 183*9a98fdf5SMartin Blumenstingl 184d4983983SMaxime Jourdan canvas->dev = dev; 185d4983983SMaxime Jourdan spin_lock_init(&canvas->lock); 186d4983983SMaxime Jourdan dev_set_drvdata(dev, canvas); 187d4983983SMaxime Jourdan 188d4983983SMaxime Jourdan return 0; 189d4983983SMaxime Jourdan } 190d4983983SMaxime Jourdan 191d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = { 192*9a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8-canvas", .data = (void *)false, }, 193*9a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8b-canvas", .data = (void *)false, }, 194*9a98fdf5SMartin Blumenstingl { .compatible = "amlogic,meson8m2-canvas", .data = (void *)false, }, 195*9a98fdf5SMartin Blumenstingl { .compatible = "amlogic,canvas", .data = (void *)true, }, 196d4983983SMaxime Jourdan {} 197d4983983SMaxime Jourdan }; 198d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match); 199d4983983SMaxime Jourdan 200d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = { 201d4983983SMaxime Jourdan .probe = meson_canvas_probe, 202d4983983SMaxime Jourdan .driver = { 203d4983983SMaxime Jourdan .name = "amlogic-canvas", 204d4983983SMaxime Jourdan .of_match_table = canvas_dt_match, 205d4983983SMaxime Jourdan }, 206d4983983SMaxime Jourdan }; 207d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver); 208d4983983SMaxime Jourdan 209d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver"); 210d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); 211d4983983SMaxime Jourdan MODULE_LICENSE("GPL"); 212