1*d4983983SMaxime Jourdan // SPDX-License-Identifier: GPL-2.0+ 2*d4983983SMaxime Jourdan /* 3*d4983983SMaxime Jourdan * Copyright (C) 2018 BayLibre, SAS 4*d4983983SMaxime Jourdan * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 5*d4983983SMaxime Jourdan * Copyright (C) 2014 Endless Mobile 6*d4983983SMaxime Jourdan */ 7*d4983983SMaxime Jourdan 8*d4983983SMaxime Jourdan #include <linux/kernel.h> 9*d4983983SMaxime Jourdan #include <linux/mfd/syscon.h> 10*d4983983SMaxime Jourdan #include <linux/module.h> 11*d4983983SMaxime Jourdan #include <linux/regmap.h> 12*d4983983SMaxime Jourdan #include <linux/soc/amlogic/meson-canvas.h> 13*d4983983SMaxime Jourdan #include <linux/of_address.h> 14*d4983983SMaxime Jourdan #include <linux/of_platform.h> 15*d4983983SMaxime Jourdan #include <linux/io.h> 16*d4983983SMaxime Jourdan 17*d4983983SMaxime Jourdan #define NUM_CANVAS 256 18*d4983983SMaxime Jourdan 19*d4983983SMaxime Jourdan /* DMC Registers */ 20*d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAL 0x00 21*d4983983SMaxime Jourdan #define CANVAS_WIDTH_LBIT 29 22*d4983983SMaxime Jourdan #define CANVAS_WIDTH_LWID 3 23*d4983983SMaxime Jourdan #define DMC_CAV_LUT_DATAH 0x04 24*d4983983SMaxime Jourdan #define CANVAS_WIDTH_HBIT 0 25*d4983983SMaxime Jourdan #define CANVAS_HEIGHT_BIT 9 26*d4983983SMaxime Jourdan #define CANVAS_WRAP_BIT 22 27*d4983983SMaxime Jourdan #define CANVAS_BLKMODE_BIT 24 28*d4983983SMaxime Jourdan #define CANVAS_ENDIAN_BIT 26 29*d4983983SMaxime Jourdan #define DMC_CAV_LUT_ADDR 0x08 30*d4983983SMaxime Jourdan #define CANVAS_LUT_WR_EN BIT(9) 31*d4983983SMaxime Jourdan #define CANVAS_LUT_RD_EN BIT(8) 32*d4983983SMaxime Jourdan 33*d4983983SMaxime Jourdan struct meson_canvas { 34*d4983983SMaxime Jourdan struct device *dev; 35*d4983983SMaxime Jourdan void __iomem *reg_base; 36*d4983983SMaxime Jourdan spinlock_t lock; /* canvas device lock */ 37*d4983983SMaxime Jourdan u8 used[NUM_CANVAS]; 38*d4983983SMaxime Jourdan }; 39*d4983983SMaxime Jourdan 40*d4983983SMaxime Jourdan static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val) 41*d4983983SMaxime Jourdan { 42*d4983983SMaxime Jourdan writel_relaxed(val, canvas->reg_base + reg); 43*d4983983SMaxime Jourdan } 44*d4983983SMaxime Jourdan 45*d4983983SMaxime Jourdan static u32 canvas_read(struct meson_canvas *canvas, u32 reg) 46*d4983983SMaxime Jourdan { 47*d4983983SMaxime Jourdan return readl_relaxed(canvas->reg_base + reg); 48*d4983983SMaxime Jourdan } 49*d4983983SMaxime Jourdan 50*d4983983SMaxime Jourdan struct meson_canvas *meson_canvas_get(struct device *dev) 51*d4983983SMaxime Jourdan { 52*d4983983SMaxime Jourdan struct device_node *canvas_node; 53*d4983983SMaxime Jourdan struct platform_device *canvas_pdev; 54*d4983983SMaxime Jourdan 55*d4983983SMaxime Jourdan canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); 56*d4983983SMaxime Jourdan if (!canvas_node) 57*d4983983SMaxime Jourdan return ERR_PTR(-ENODEV); 58*d4983983SMaxime Jourdan 59*d4983983SMaxime Jourdan canvas_pdev = of_find_device_by_node(canvas_node); 60*d4983983SMaxime Jourdan if (!canvas_pdev) 61*d4983983SMaxime Jourdan return ERR_PTR(-EPROBE_DEFER); 62*d4983983SMaxime Jourdan 63*d4983983SMaxime Jourdan return dev_get_drvdata(&canvas_pdev->dev); 64*d4983983SMaxime Jourdan } 65*d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_get); 66*d4983983SMaxime Jourdan 67*d4983983SMaxime Jourdan int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index, 68*d4983983SMaxime Jourdan u32 addr, u32 stride, u32 height, 69*d4983983SMaxime Jourdan unsigned int wrap, 70*d4983983SMaxime Jourdan unsigned int blkmode, 71*d4983983SMaxime Jourdan unsigned int endian) 72*d4983983SMaxime Jourdan { 73*d4983983SMaxime Jourdan unsigned long flags; 74*d4983983SMaxime Jourdan 75*d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 76*d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 77*d4983983SMaxime Jourdan dev_err(canvas->dev, 78*d4983983SMaxime Jourdan "Trying to setup non allocated canvas %u\n", 79*d4983983SMaxime Jourdan canvas_index); 80*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 81*d4983983SMaxime Jourdan return -EINVAL; 82*d4983983SMaxime Jourdan } 83*d4983983SMaxime Jourdan 84*d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAL, 85*d4983983SMaxime Jourdan ((addr + 7) >> 3) | 86*d4983983SMaxime Jourdan (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); 87*d4983983SMaxime Jourdan 88*d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_DATAH, 89*d4983983SMaxime Jourdan ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << 90*d4983983SMaxime Jourdan CANVAS_WIDTH_HBIT) | 91*d4983983SMaxime Jourdan (height << CANVAS_HEIGHT_BIT) | 92*d4983983SMaxime Jourdan (wrap << CANVAS_WRAP_BIT) | 93*d4983983SMaxime Jourdan (blkmode << CANVAS_BLKMODE_BIT) | 94*d4983983SMaxime Jourdan (endian << CANVAS_ENDIAN_BIT)); 95*d4983983SMaxime Jourdan 96*d4983983SMaxime Jourdan canvas_write(canvas, DMC_CAV_LUT_ADDR, 97*d4983983SMaxime Jourdan CANVAS_LUT_WR_EN | canvas_index); 98*d4983983SMaxime Jourdan 99*d4983983SMaxime Jourdan /* Force a read-back to make sure everything is flushed. */ 100*d4983983SMaxime Jourdan canvas_read(canvas, DMC_CAV_LUT_DATAH); 101*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 102*d4983983SMaxime Jourdan 103*d4983983SMaxime Jourdan return 0; 104*d4983983SMaxime Jourdan } 105*d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_config); 106*d4983983SMaxime Jourdan 107*d4983983SMaxime Jourdan int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index) 108*d4983983SMaxime Jourdan { 109*d4983983SMaxime Jourdan int i; 110*d4983983SMaxime Jourdan unsigned long flags; 111*d4983983SMaxime Jourdan 112*d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 113*d4983983SMaxime Jourdan for (i = 0; i < NUM_CANVAS; ++i) { 114*d4983983SMaxime Jourdan if (!canvas->used[i]) { 115*d4983983SMaxime Jourdan canvas->used[i] = 1; 116*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 117*d4983983SMaxime Jourdan *canvas_index = i; 118*d4983983SMaxime Jourdan return 0; 119*d4983983SMaxime Jourdan } 120*d4983983SMaxime Jourdan } 121*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 122*d4983983SMaxime Jourdan 123*d4983983SMaxime Jourdan dev_err(canvas->dev, "No more canvas available\n"); 124*d4983983SMaxime Jourdan return -ENODEV; 125*d4983983SMaxime Jourdan } 126*d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_alloc); 127*d4983983SMaxime Jourdan 128*d4983983SMaxime Jourdan int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index) 129*d4983983SMaxime Jourdan { 130*d4983983SMaxime Jourdan unsigned long flags; 131*d4983983SMaxime Jourdan 132*d4983983SMaxime Jourdan spin_lock_irqsave(&canvas->lock, flags); 133*d4983983SMaxime Jourdan if (!canvas->used[canvas_index]) { 134*d4983983SMaxime Jourdan dev_err(canvas->dev, 135*d4983983SMaxime Jourdan "Trying to free unused canvas %u\n", canvas_index); 136*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 137*d4983983SMaxime Jourdan return -EINVAL; 138*d4983983SMaxime Jourdan } 139*d4983983SMaxime Jourdan canvas->used[canvas_index] = 0; 140*d4983983SMaxime Jourdan spin_unlock_irqrestore(&canvas->lock, flags); 141*d4983983SMaxime Jourdan 142*d4983983SMaxime Jourdan return 0; 143*d4983983SMaxime Jourdan } 144*d4983983SMaxime Jourdan EXPORT_SYMBOL_GPL(meson_canvas_free); 145*d4983983SMaxime Jourdan 146*d4983983SMaxime Jourdan static int meson_canvas_probe(struct platform_device *pdev) 147*d4983983SMaxime Jourdan { 148*d4983983SMaxime Jourdan struct resource *res; 149*d4983983SMaxime Jourdan struct meson_canvas *canvas; 150*d4983983SMaxime Jourdan struct device *dev = &pdev->dev; 151*d4983983SMaxime Jourdan 152*d4983983SMaxime Jourdan canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL); 153*d4983983SMaxime Jourdan if (!canvas) 154*d4983983SMaxime Jourdan return -ENOMEM; 155*d4983983SMaxime Jourdan 156*d4983983SMaxime Jourdan res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 157*d4983983SMaxime Jourdan canvas->reg_base = devm_ioremap_resource(dev, res); 158*d4983983SMaxime Jourdan if (IS_ERR(canvas->reg_base)) 159*d4983983SMaxime Jourdan return PTR_ERR(canvas->reg_base); 160*d4983983SMaxime Jourdan 161*d4983983SMaxime Jourdan canvas->dev = dev; 162*d4983983SMaxime Jourdan spin_lock_init(&canvas->lock); 163*d4983983SMaxime Jourdan dev_set_drvdata(dev, canvas); 164*d4983983SMaxime Jourdan 165*d4983983SMaxime Jourdan return 0; 166*d4983983SMaxime Jourdan } 167*d4983983SMaxime Jourdan 168*d4983983SMaxime Jourdan static const struct of_device_id canvas_dt_match[] = { 169*d4983983SMaxime Jourdan { .compatible = "amlogic,canvas" }, 170*d4983983SMaxime Jourdan {} 171*d4983983SMaxime Jourdan }; 172*d4983983SMaxime Jourdan MODULE_DEVICE_TABLE(of, canvas_dt_match); 173*d4983983SMaxime Jourdan 174*d4983983SMaxime Jourdan static struct platform_driver meson_canvas_driver = { 175*d4983983SMaxime Jourdan .probe = meson_canvas_probe, 176*d4983983SMaxime Jourdan .driver = { 177*d4983983SMaxime Jourdan .name = "amlogic-canvas", 178*d4983983SMaxime Jourdan .of_match_table = canvas_dt_match, 179*d4983983SMaxime Jourdan }, 180*d4983983SMaxime Jourdan }; 181*d4983983SMaxime Jourdan module_platform_driver(meson_canvas_driver); 182*d4983983SMaxime Jourdan 183*d4983983SMaxime Jourdan MODULE_DESCRIPTION("Amlogic Canvas driver"); 184*d4983983SMaxime Jourdan MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); 185*d4983983SMaxime Jourdan MODULE_LICENSE("GPL"); 186