cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

wm8505fb.c (10281B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  WonderMedia WM8505 Frame Buffer device driver
      4 *
      5 *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
      6 *    Based on vt8500lcdfb.c
      7 */
      8
      9#include <linux/delay.h>
     10#include <linux/dma-mapping.h>
     11#include <linux/fb.h>
     12#include <linux/errno.h>
     13#include <linux/err.h>
     14#include <linux/init.h>
     15#include <linux/interrupt.h>
     16#include <linux/io.h>
     17#include <linux/kernel.h>
     18#include <linux/memblock.h>
     19#include <linux/mm.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/of_fdt.h>
     23#include <linux/platform_device.h>
     24#include <linux/slab.h>
     25#include <linux/string.h>
     26#include <linux/wait.h>
     27#include <video/of_display_timing.h>
     28
     29#include "wm8505fb_regs.h"
     30#include "wmt_ge_rops.h"
     31
     32#define DRIVER_NAME "wm8505-fb"
     33
     34#define to_wm8505fb_info(__info) container_of(__info, \
     35						struct wm8505fb_info, fb)
     36struct wm8505fb_info {
     37	struct fb_info		fb;
     38	void __iomem		*regbase;
     39	unsigned int		contrast;
     40};
     41
     42
     43static int wm8505fb_init_hw(struct fb_info *info)
     44{
     45	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
     46
     47	int i;
     48
     49	/* I know the purpose only of few registers, so clear unknown */
     50	for (i = 0; i < 0x200; i += 4)
     51		writel(0, fbi->regbase + i);
     52
     53	/* Set frame buffer address */
     54	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
     55	writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
     56
     57	/*
     58	 * Set in-memory picture format to RGB
     59	 * 0x31C sets the correct color mode (RGB565) for WM8650
     60	 * Bit 8+9 (0x300) are ignored on WM8505 as reserved
     61	 */
     62	writel(0x31c,		       fbi->regbase + WMT_GOVR_COLORSPACE);
     63	writel(1,		       fbi->regbase + WMT_GOVR_COLORSPACE1);
     64
     65	/* Virtual buffer size */
     66	writel(info->var.xres,	       fbi->regbase + WMT_GOVR_XRES);
     67	writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
     68
     69	/* black magic ;) */
     70	writel(0xf,		       fbi->regbase + WMT_GOVR_FHI);
     71	writel(4,		       fbi->regbase + WMT_GOVR_DVO_SET);
     72	writel(1,		       fbi->regbase + WMT_GOVR_MIF_ENABLE);
     73	writel(1,		       fbi->regbase + WMT_GOVR_REG_UPDATE);
     74
     75	return 0;
     76}
     77
     78static int wm8505fb_set_timing(struct fb_info *info)
     79{
     80	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
     81
     82	int h_start = info->var.left_margin;
     83	int h_end = h_start + info->var.xres;
     84	int h_all = h_end + info->var.right_margin;
     85	int h_sync = info->var.hsync_len;
     86
     87	int v_start = info->var.upper_margin;
     88	int v_end = v_start + info->var.yres;
     89	int v_all = v_end + info->var.lower_margin;
     90	int v_sync = info->var.vsync_len;
     91
     92	writel(0, fbi->regbase + WMT_GOVR_TG);
     93
     94	writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
     95	writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
     96	writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
     97	writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
     98
     99	writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
    100	writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
    101	writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
    102	writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
    103
    104	writel(1, fbi->regbase + WMT_GOVR_TG);
    105
    106	return 0;
    107}
    108
    109
    110static int wm8505fb_set_par(struct fb_info *info)
    111{
    112	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    113
    114	if (!fbi)
    115		return -EINVAL;
    116
    117	if (info->var.bits_per_pixel == 32) {
    118		info->var.red.offset = 16;
    119		info->var.red.length = 8;
    120		info->var.red.msb_right = 0;
    121		info->var.green.offset = 8;
    122		info->var.green.length = 8;
    123		info->var.green.msb_right = 0;
    124		info->var.blue.offset = 0;
    125		info->var.blue.length = 8;
    126		info->var.blue.msb_right = 0;
    127		info->fix.visual = FB_VISUAL_TRUECOLOR;
    128		info->fix.line_length = info->var.xres_virtual << 2;
    129	} else if (info->var.bits_per_pixel == 16) {
    130		info->var.red.offset = 11;
    131		info->var.red.length = 5;
    132		info->var.red.msb_right = 0;
    133		info->var.green.offset = 5;
    134		info->var.green.length = 6;
    135		info->var.green.msb_right = 0;
    136		info->var.blue.offset = 0;
    137		info->var.blue.length = 5;
    138		info->var.blue.msb_right = 0;
    139		info->fix.visual = FB_VISUAL_TRUECOLOR;
    140		info->fix.line_length = info->var.xres_virtual << 1;
    141	}
    142
    143	wm8505fb_set_timing(info);
    144
    145	writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
    146		fbi->regbase + WMT_GOVR_CONTRAST);
    147
    148	return 0;
    149}
    150
    151static ssize_t contrast_show(struct device *dev,
    152			     struct device_attribute *attr, char *buf)
    153{
    154	struct fb_info *info = dev_get_drvdata(dev);
    155	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    156
    157	return sprintf(buf, "%u\n", fbi->contrast);
    158}
    159
    160static ssize_t contrast_store(struct device *dev,
    161			      struct device_attribute *attr,
    162			      const char *buf, size_t count)
    163{
    164	struct fb_info *info = dev_get_drvdata(dev);
    165	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    166	unsigned long tmp;
    167
    168	if (kstrtoul(buf, 10, &tmp) || (tmp > 0xff))
    169		return -EINVAL;
    170	fbi->contrast = tmp;
    171
    172	wm8505fb_set_par(info);
    173
    174	return count;
    175}
    176
    177static DEVICE_ATTR_RW(contrast);
    178
    179static struct attribute *wm8505fb_attrs[] = {
    180	&dev_attr_contrast.attr,
    181	NULL,
    182};
    183ATTRIBUTE_GROUPS(wm8505fb);
    184
    185static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
    186{
    187	chan &= 0xffff;
    188	chan >>= 16 - bf->length;
    189	return chan << bf->offset;
    190}
    191
    192static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
    193			   unsigned blue, unsigned transp,
    194			   struct fb_info *info) {
    195	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    196	int ret = 1;
    197	unsigned int val;
    198	if (regno >= 256)
    199		return -EINVAL;
    200
    201	if (info->var.grayscale)
    202		red = green = blue =
    203			(19595 * red + 38470 * green + 7471 * blue) >> 16;
    204
    205	switch (fbi->fb.fix.visual) {
    206	case FB_VISUAL_TRUECOLOR:
    207		if (regno < 16) {
    208			u32 *pal = info->pseudo_palette;
    209
    210			val  = chan_to_field(red, &fbi->fb.var.red);
    211			val |= chan_to_field(green, &fbi->fb.var.green);
    212			val |= chan_to_field(blue, &fbi->fb.var.blue);
    213
    214			pal[regno] = val;
    215			ret = 0;
    216		}
    217		break;
    218	}
    219
    220	return ret;
    221}
    222
    223static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
    224				struct fb_info *info)
    225{
    226	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    227
    228	writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
    229	writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
    230	return 0;
    231}
    232
    233static int wm8505fb_blank(int blank, struct fb_info *info)
    234{
    235	struct wm8505fb_info *fbi = to_wm8505fb_info(info);
    236
    237	switch (blank) {
    238	case FB_BLANK_UNBLANK:
    239		wm8505fb_set_timing(info);
    240		break;
    241	default:
    242		writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
    243		break;
    244	}
    245
    246	return 0;
    247}
    248
    249static const struct fb_ops wm8505fb_ops = {
    250	.owner		= THIS_MODULE,
    251	.fb_set_par	= wm8505fb_set_par,
    252	.fb_setcolreg	= wm8505fb_setcolreg,
    253	.fb_fillrect	= wmt_ge_fillrect,
    254	.fb_copyarea	= wmt_ge_copyarea,
    255	.fb_imageblit	= sys_imageblit,
    256	.fb_sync	= wmt_ge_sync,
    257	.fb_pan_display	= wm8505fb_pan_display,
    258	.fb_blank	= wm8505fb_blank,
    259};
    260
    261static int wm8505fb_probe(struct platform_device *pdev)
    262{
    263	struct wm8505fb_info	*fbi;
    264	struct resource	*res;
    265	struct display_timings *disp_timing;
    266	void			*addr;
    267	int ret;
    268
    269	struct fb_videomode	mode;
    270	u32			bpp;
    271	dma_addr_t fb_mem_phys;
    272	unsigned long fb_mem_len;
    273	void *fb_mem_virt;
    274
    275	fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) +
    276			sizeof(u32) * 16, GFP_KERNEL);
    277	if (!fbi)
    278		return -ENOMEM;
    279
    280	strcpy(fbi->fb.fix.id, DRIVER_NAME);
    281
    282	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
    283	fbi->fb.fix.xpanstep	= 1;
    284	fbi->fb.fix.ypanstep	= 1;
    285	fbi->fb.fix.ywrapstep	= 0;
    286	fbi->fb.fix.accel	= FB_ACCEL_NONE;
    287
    288	fbi->fb.fbops		= &wm8505fb_ops;
    289	fbi->fb.flags		= FBINFO_DEFAULT
    290				| FBINFO_HWACCEL_COPYAREA
    291				| FBINFO_HWACCEL_FILLRECT
    292				| FBINFO_HWACCEL_XPAN
    293				| FBINFO_HWACCEL_YPAN
    294				| FBINFO_VIRTFB
    295				| FBINFO_PARTIAL_PAN_OK;
    296	fbi->fb.node		= -1;
    297
    298	addr = fbi;
    299	addr = addr + sizeof(struct wm8505fb_info);
    300	fbi->fb.pseudo_palette	= addr;
    301
    302	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    303	fbi->regbase = devm_ioremap_resource(&pdev->dev, res);
    304	if (IS_ERR(fbi->regbase))
    305		return PTR_ERR(fbi->regbase);
    306
    307	disp_timing = of_get_display_timings(pdev->dev.of_node);
    308	if (!disp_timing)
    309		return -EINVAL;
    310
    311	ret = of_get_fb_videomode(pdev->dev.of_node, &mode, OF_USE_NATIVE_MODE);
    312	if (ret)
    313		return ret;
    314
    315	ret = of_property_read_u32(pdev->dev.of_node, "bits-per-pixel", &bpp);
    316	if (ret)
    317		return ret;
    318
    319	fb_videomode_to_var(&fbi->fb.var, &mode);
    320
    321	fbi->fb.var.nonstd		= 0;
    322	fbi->fb.var.activate		= FB_ACTIVATE_NOW;
    323
    324	fbi->fb.var.height		= -1;
    325	fbi->fb.var.width		= -1;
    326
    327	/* try allocating the framebuffer */
    328	fb_mem_len = mode.xres * mode.yres * 2 * (bpp / 8);
    329	fb_mem_virt = dmam_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
    330				GFP_KERNEL);
    331	if (!fb_mem_virt) {
    332		pr_err("%s: Failed to allocate framebuffer\n", __func__);
    333		return -ENOMEM;
    334	}
    335
    336	fbi->fb.var.xres_virtual	= mode.xres;
    337	fbi->fb.var.yres_virtual	= mode.yres * 2;
    338	fbi->fb.var.bits_per_pixel	= bpp;
    339
    340	fbi->fb.fix.smem_start		= fb_mem_phys;
    341	fbi->fb.fix.smem_len		= fb_mem_len;
    342	fbi->fb.screen_buffer		= fb_mem_virt;
    343	fbi->fb.screen_size		= fb_mem_len;
    344
    345	fbi->contrast = 0x10;
    346	ret = wm8505fb_set_par(&fbi->fb);
    347	if (ret) {
    348		dev_err(&pdev->dev, "Failed to set parameters\n");
    349		return ret;
    350	}
    351
    352	if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
    353		dev_err(&pdev->dev, "Failed to allocate color map\n");
    354		return -ENOMEM;
    355	}
    356
    357	wm8505fb_init_hw(&fbi->fb);
    358
    359	platform_set_drvdata(pdev, fbi);
    360
    361	ret = register_framebuffer(&fbi->fb);
    362	if (ret < 0) {
    363		dev_err(&pdev->dev,
    364			"Failed to register framebuffer device: %d\n", ret);
    365		if (fbi->fb.cmap.len)
    366			fb_dealloc_cmap(&fbi->fb.cmap);
    367		return ret;
    368	}
    369
    370	fb_info(&fbi->fb, "%s frame buffer at 0x%lx-0x%lx\n",
    371		fbi->fb.fix.id, fbi->fb.fix.smem_start,
    372		fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
    373
    374	return 0;
    375}
    376
    377static int wm8505fb_remove(struct platform_device *pdev)
    378{
    379	struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
    380
    381	unregister_framebuffer(&fbi->fb);
    382
    383	writel(0, fbi->regbase);
    384
    385	if (fbi->fb.cmap.len)
    386		fb_dealloc_cmap(&fbi->fb.cmap);
    387
    388	return 0;
    389}
    390
    391static const struct of_device_id wmt_dt_ids[] = {
    392	{ .compatible = "wm,wm8505-fb", },
    393	{}
    394};
    395
    396static struct platform_driver wm8505fb_driver = {
    397	.probe		= wm8505fb_probe,
    398	.remove		= wm8505fb_remove,
    399	.driver		= {
    400		.name	= DRIVER_NAME,
    401		.of_match_table = wmt_dt_ids,
    402		.dev_groups	= wm8505fb_groups,
    403	},
    404};
    405
    406module_platform_driver(wm8505fb_driver);
    407
    408MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
    409MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
    410MODULE_LICENSE("GPL v2");
    411MODULE_DEVICE_TABLE(of, wmt_dt_ids);