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