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]; 38d4983983SMaxime Jourdan }; 39d4983983SMaxime Jourdan 40d4983983SMaxime Jourdan static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val) 41d4983983SMaxime Jourdan { 42d4983983SMaxime Jourdan writel_relaxed(val, canvas->reg_base + reg); 43d4983983SMaxime Jourdan } 44d4983983SMaxime Jourdan 45d4983983SMaxime Jourdan static u32 canvas_read(struct meson_canvas *canvas, u32 reg) 46d4983983SMaxime Jourdan { 47d4983983SMaxime Jourdan return readl_relaxed(canvas->reg_base + reg); 48d4983983SMaxime Jourdan } 49d4983983SMaxime Jourdan 50d4983983SMaxime Jourdan struct meson_canvas *meson_canvas_get(struct device *dev) 51d4983983SMaxime Jourdan { 52d4983983SMaxime Jourdan struct device_node *canvas_node; 53d4983983SMaxime Jourdan struct platform_device *canvas_pdev; 54d4983983SMaxime Jourdan 55d4983983SMaxime Jourdan canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); 56d4983983SMaxime Jourdan if (!canvas_node) 57d4983983SMaxime Jourdan return ERR_PTR(-ENODEV); 58d4983983SMaxime Jourdan 59d4983983SMaxime Jourdan canvas_pdev = of_find_device_by_node(canvas_node); 60*99e5a8dfSwen yang if (!canvas_pdev) { 61*99e5a8dfSwen yang of_node_put(canvas_node); 62d4983983SMaxime Jourdan return ERR_PTR(-EPROBE_DEFER); 63*99e5a8dfSwen yang } 64d4983983SMaxime Jourdan 65*99e5a8dfSwen yang of_node_put(canvas_node); 66d4983983SMaxime Jourdan return dev_get_drvdata(&canvas_pdev->dev); 67d4983983SMaxime Jourdan } 68d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get); 69d4983983SMaxime Jourdan 70d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, 71d4983983SMaxime Jourdan u32 addr, u32 stride, u32 height, 72d4983983SMaxime Jourdan unsigned int wrap, 73d4983983SMaxime Jourdan unsigned int blkmode, 74d4983983SMaxime Jourdan unsigned int endian) 75d4983983SMaxime Jourdan { 76d4983983SMaxime Jourdan unsigned long flags; 77d4983983SMaxime Jourdan 78d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 79d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 80d4983983SMaxime Jourdan dev_err(canvas->dev, 81d4983983SMaxime Jourdan "Trying to setup non allocated canvas %u\n", 82d4983983SMaxime Jourdan canvas_index); 83d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 84d4983983SMaxime Jourdan return -EINVAL; 85d4983983SMaxime Jourdan } 86d4983983SMaxime Jourdan 87d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAL, 88d4983983SMaxime Jourdan ((addr + 7) >> 3) | 89d4983983SMaxime Jourdan (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); 90d4983983SMaxime Jourdan 91d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAH, 92d4983983SMaxime Jourdan ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << 93d4983983SMaxime Jourdan CANVAS_WIDTH_HBIT) | 94d4983983SMaxime Jourdan (height << CANVAS_HEIGHT_BIT) | 95d4983983SMaxime Jourdan (wrap << CANVAS_WRAP_BIT) | 96d4983983SMaxime Jourdan (blkmode << CANVAS_BLKMODE_BIT) | 97d4983983SMaxime Jourdan (endian << CANVAS_ENDIAN_BIT)); 98d4983983SMaxime Jourdan 99d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_ADDR, 100d4983983SMaxime Jourdan CANVAS_LUT_WR_EN | canvas_index); 101d4983983SMaxime Jourdan 102d4983983SMaxime Jourdan /* Force a read-back to make sure everything is flushed. */ 103d4983983SMaxime Jourdan canvas_read(canvas, DMC_CAV_LUT_DATAH); 104d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 105d4983983SMaxime Jourdan 106d4983983SMaxime Jourdan return 0; 107d4983983SMaxime Jourdan } 108d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config); 109d4983983SMaxime Jourdan 110d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index) 111d4983983SMaxime Jourdan { 112d4983983SMaxime Jourdan int i; 113d4983983SMaxime Jourdan unsigned long flags; 114d4983983SMaxime Jourdan 115d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 116d4983983SMaxime Jourdan for (i = 0; i < NUM_CANVAS; ++i) { 117d4983983SMaxime Jourdan if (!canvas->used[i]) { 118d4983983SMaxime Jourdan canvas->used[i] = 1; 119d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 120d4983983SMaxime Jourdan *canvas_index = i; 121d4983983SMaxime Jourdan return 0; 122d4983983SMaxime Jourdan } 123d4983983SMaxime Jourdan } 124d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 125d4983983SMaxime Jourdan 126d4983983SMaxime Jourdan dev_err(canvas->dev, "No more canvas available\n"); 127d4983983SMaxime Jourdan return -ENODEV; 128d4983983SMaxime Jourdan } 129d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc); 130d4983983SMaxime Jourdan 131d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index) 132d4983983SMaxime Jourdan { 133d4983983SMaxime Jourdan unsigned long flags; 134d4983983SMaxime Jourdan 135d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 136d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 137d4983983SMaxime Jourdan dev_err(canvas->dev, 138d4983983SMaxime Jourdan "Trying to free unused canvas %u\n", canvas_index); 139d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 140d4983983SMaxime Jourdan return -EINVAL; 141d4983983SMaxime Jourdan } 142d4983983SMaxime Jourdan canvas->used[canvas_index] = 0; 143d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 144d4983983SMaxime Jourdan 145d4983983SMaxime Jourdan return 0; 146d4983983SMaxime Jourdan } 147d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free); 148d4983983SMaxime Jourdan 149d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev) 150d4983983SMaxime Jourdan { 151d4983983SMaxime Jourdan struct resource *res; 152d4983983SMaxime Jourdan struct meson_canvas *canvas; 153d4983983SMaxime Jourdan struct device *dev = &pdev->dev; 154d4983983SMaxime Jourdan 155d4983983SMaxime Jourdan canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL); 156d4983983SMaxime Jourdan if (!canvas) 157d4983983SMaxime Jourdan return -ENOMEM; 158d4983983SMaxime Jourdan 159d4983983SMaxime Jourdan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 160d4983983SMaxime Jourdan canvas->reg_base = devm_ioremap_resource(dev, res); 161d4983983SMaxime Jourdan if (IS_ERR(canvas->reg_base)) 162d4983983SMaxime Jourdan return PTR_ERR(canvas->reg_base); 163d4983983SMaxime Jourdan 164d4983983SMaxime Jourdan canvas->dev = dev; 165d4983983SMaxime Jourdan spin_lock_init(&canvas->lock); 166d4983983SMaxime Jourdan dev_set_drvdata(dev, canvas); 167d4983983SMaxime Jourdan 168d4983983SMaxime Jourdan return 0; 169d4983983SMaxime Jourdan } 170d4983983SMaxime Jourdan 171d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = { 172d4983983SMaxime Jourdan { .compatible = "amlogic,canvas" }, 173d4983983SMaxime Jourdan {} 174d4983983SMaxime Jourdan }; 175d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match); 176d4983983SMaxime Jourdan 177d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = { 178d4983983SMaxime Jourdan .probe = meson_canvas_probe, 179d4983983SMaxime Jourdan .driver = { 180d4983983SMaxime Jourdan .name = "amlogic-canvas", 181d4983983SMaxime Jourdan .of_match_table = canvas_dt_match, 182d4983983SMaxime Jourdan }, 183d4983983SMaxime Jourdan }; 184d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver); 185d4983983SMaxime Jourdan 186d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver"); 187d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); 188d4983983SMaxime Jourdan MODULE_LICENSE("GPL"); 189