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