18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Cirrus Logic CLPS711X FB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> 68c2ecf20Sopenharmony_ci * Based on driver by Russell King <rmk@arm.linux.org.uk> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/fb.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/lcd.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/clps711x.h> 198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 208c2ecf20Sopenharmony_ci#include <video/of_display_timing.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define CLPS711X_FB_NAME "clps711x-fb" 238c2ecf20Sopenharmony_ci#define CLPS711X_FB_BPP_MAX (4) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Registers relative to LCDCON */ 268c2ecf20Sopenharmony_ci#define CLPS711X_LCDCON (0x0000) 278c2ecf20Sopenharmony_ci# define LCDCON_GSEN BIT(30) 288c2ecf20Sopenharmony_ci# define LCDCON_GSMD BIT(31) 298c2ecf20Sopenharmony_ci#define CLPS711X_PALLSW (0x0280) 308c2ecf20Sopenharmony_ci#define CLPS711X_PALMSW (0x02c0) 318c2ecf20Sopenharmony_ci#define CLPS711X_FBADDR (0x0d40) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct clps711x_fb_info { 348c2ecf20Sopenharmony_ci struct clk *clk; 358c2ecf20Sopenharmony_ci void __iomem *base; 368c2ecf20Sopenharmony_ci struct regmap *syscon; 378c2ecf20Sopenharmony_ci resource_size_t buffsize; 388c2ecf20Sopenharmony_ci struct fb_videomode mode; 398c2ecf20Sopenharmony_ci struct regulator *lcd_pwr; 408c2ecf20Sopenharmony_ci u32 ac_prescale; 418c2ecf20Sopenharmony_ci bool cmap_invert; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int clps711x_fb_setcolreg(u_int regno, u_int red, u_int green, 458c2ecf20Sopenharmony_ci u_int blue, u_int transp, struct fb_info *info) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = info->par; 488c2ecf20Sopenharmony_ci u32 level, mask, shift; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (regno >= BIT(info->var.bits_per_pixel)) 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci shift = 4 * (regno & 7); 548c2ecf20Sopenharmony_ci mask = 0xf << shift; 558c2ecf20Sopenharmony_ci /* gray = 0.30*R + 0.58*G + 0.11*B */ 568c2ecf20Sopenharmony_ci level = (((red * 77 + green * 151 + blue * 28) >> 20) << shift) & mask; 578c2ecf20Sopenharmony_ci if (cfb->cmap_invert) 588c2ecf20Sopenharmony_ci level = 0xf - level; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci regno = (regno < 8) ? CLPS711X_PALLSW : CLPS711X_PALMSW; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci writel((readl(cfb->base + regno) & ~mask) | level, cfb->base + regno); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int clps711x_fb_check_var(struct fb_var_screeninfo *var, 688c2ecf20Sopenharmony_ci struct fb_info *info) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u32 val; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (var->bits_per_pixel < 1 || 738c2ecf20Sopenharmony_ci var->bits_per_pixel > CLPS711X_FB_BPP_MAX) 748c2ecf20Sopenharmony_ci return -EINVAL; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (!var->pixclock) 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(var->xres, 16) - 1; 808c2ecf20Sopenharmony_ci if (val < 0x01 || val > 0x3f) 818c2ecf20Sopenharmony_ci return -EINVAL; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(var->yres * var->xres * var->bits_per_pixel, 128); 848c2ecf20Sopenharmony_ci val--; 858c2ecf20Sopenharmony_ci if (val < 0x001 || val > 0x1fff) 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 898c2ecf20Sopenharmony_ci var->transp.offset = 0; 908c2ecf20Sopenharmony_ci var->transp.length = 0; 918c2ecf20Sopenharmony_ci var->red.msb_right = 0; 928c2ecf20Sopenharmony_ci var->red.offset = 0; 938c2ecf20Sopenharmony_ci var->red.length = var->bits_per_pixel; 948c2ecf20Sopenharmony_ci var->green = var->red; 958c2ecf20Sopenharmony_ci var->blue = var->red; 968c2ecf20Sopenharmony_ci var->grayscale = var->bits_per_pixel > 1; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int clps711x_fb_set_par(struct fb_info *info) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = info->par; 1048c2ecf20Sopenharmony_ci resource_size_t size; 1058c2ecf20Sopenharmony_ci u32 lcdcon, pps; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci size = (info->var.xres * info->var.yres * info->var.bits_per_pixel) / 8; 1088c2ecf20Sopenharmony_ci if (size > cfb->buffsize) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 1128c2ecf20Sopenharmony_ci case 1: 1138c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_MONO01; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case 2: 1168c2ecf20Sopenharmony_ci case 4: 1178c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; 1248c2ecf20Sopenharmony_ci info->fix.smem_len = size; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci lcdcon = (info->var.xres * info->var.yres * 1278c2ecf20Sopenharmony_ci info->var.bits_per_pixel) / 128 - 1; 1288c2ecf20Sopenharmony_ci lcdcon |= ((info->var.xres / 16) - 1) << 13; 1298c2ecf20Sopenharmony_ci lcdcon |= (cfb->ac_prescale & 0x1f) << 25; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci pps = clk_get_rate(cfb->clk) / (PICOS2KHZ(info->var.pixclock) * 1000); 1328c2ecf20Sopenharmony_ci if (pps) 1338c2ecf20Sopenharmony_ci pps--; 1348c2ecf20Sopenharmony_ci lcdcon |= (pps & 0x3f) << 19; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 4) 1378c2ecf20Sopenharmony_ci lcdcon |= LCDCON_GSMD; 1388c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel >= 2) 1398c2ecf20Sopenharmony_ci lcdcon |= LCDCON_GSEN; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* LCDCON must only be changed while the LCD is disabled */ 1428c2ecf20Sopenharmony_ci regmap_update_bits(cfb->syscon, SYSCON_OFFSET, SYSCON1_LCDEN, 0); 1438c2ecf20Sopenharmony_ci writel(lcdcon, cfb->base + CLPS711X_LCDCON); 1448c2ecf20Sopenharmony_ci regmap_update_bits(cfb->syscon, SYSCON_OFFSET, 1458c2ecf20Sopenharmony_ci SYSCON1_LCDEN, SYSCON1_LCDEN); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int clps711x_fb_blank(int blank, struct fb_info *info) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci /* Return happy */ 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct fb_ops clps711x_fb_ops = { 1578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1588c2ecf20Sopenharmony_ci .fb_setcolreg = clps711x_fb_setcolreg, 1598c2ecf20Sopenharmony_ci .fb_check_var = clps711x_fb_check_var, 1608c2ecf20Sopenharmony_ci .fb_set_par = clps711x_fb_set_par, 1618c2ecf20Sopenharmony_ci .fb_blank = clps711x_fb_blank, 1628c2ecf20Sopenharmony_ci .fb_fillrect = sys_fillrect, 1638c2ecf20Sopenharmony_ci .fb_copyarea = sys_copyarea, 1648c2ecf20Sopenharmony_ci .fb_imageblit = sys_imageblit, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int clps711x_lcd_check_fb(struct lcd_device *lcddev, struct fb_info *fi) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = dev_get_drvdata(&lcddev->dev); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return (!fi || fi->par == cfb) ? 1 : 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int clps711x_lcd_get_power(struct lcd_device *lcddev) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = dev_get_drvdata(&lcddev->dev); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(cfb->lcd_pwr)) 1798c2ecf20Sopenharmony_ci if (!regulator_is_enabled(cfb->lcd_pwr)) 1808c2ecf20Sopenharmony_ci return FB_BLANK_NORMAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return FB_BLANK_UNBLANK; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int clps711x_lcd_set_power(struct lcd_device *lcddev, int blank) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = dev_get_drvdata(&lcddev->dev); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(cfb->lcd_pwr)) { 1908c2ecf20Sopenharmony_ci if (blank == FB_BLANK_UNBLANK) { 1918c2ecf20Sopenharmony_ci if (!regulator_is_enabled(cfb->lcd_pwr)) 1928c2ecf20Sopenharmony_ci return regulator_enable(cfb->lcd_pwr); 1938c2ecf20Sopenharmony_ci } else { 1948c2ecf20Sopenharmony_ci if (regulator_is_enabled(cfb->lcd_pwr)) 1958c2ecf20Sopenharmony_ci return regulator_disable(cfb->lcd_pwr); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic struct lcd_ops clps711x_lcd_ops = { 2038c2ecf20Sopenharmony_ci .check_fb = clps711x_lcd_check_fb, 2048c2ecf20Sopenharmony_ci .get_power = clps711x_lcd_get_power, 2058c2ecf20Sopenharmony_ci .set_power = clps711x_lcd_set_power, 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int clps711x_fb_probe(struct platform_device *pdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2118c2ecf20Sopenharmony_ci struct device_node *disp, *np = dev->of_node; 2128c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb; 2138c2ecf20Sopenharmony_ci struct lcd_device *lcd; 2148c2ecf20Sopenharmony_ci struct fb_info *info; 2158c2ecf20Sopenharmony_ci struct resource *res; 2168c2ecf20Sopenharmony_ci int ret = -ENOENT; 2178c2ecf20Sopenharmony_ci u32 val; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (fb_get_options(CLPS711X_FB_NAME, NULL)) 2208c2ecf20Sopenharmony_ci return -ENODEV; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(*cfb), dev); 2238c2ecf20Sopenharmony_ci if (!info) 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci cfb = info->par; 2278c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2308c2ecf20Sopenharmony_ci if (!res) 2318c2ecf20Sopenharmony_ci goto out_fb_release; 2328c2ecf20Sopenharmony_ci cfb->base = devm_ioremap(dev, res->start, resource_size(res)); 2338c2ecf20Sopenharmony_ci if (!cfb->base) { 2348c2ecf20Sopenharmony_ci ret = -ENOMEM; 2358c2ecf20Sopenharmony_ci goto out_fb_release; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci info->fix.mmio_start = res->start; 2398c2ecf20Sopenharmony_ci info->fix.mmio_len = resource_size(res); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 2428c2ecf20Sopenharmony_ci info->screen_base = devm_ioremap_resource(dev, res); 2438c2ecf20Sopenharmony_ci if (IS_ERR(info->screen_base)) { 2448c2ecf20Sopenharmony_ci ret = PTR_ERR(info->screen_base); 2458c2ecf20Sopenharmony_ci goto out_fb_release; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Physical address should be aligned to 256 MiB */ 2498c2ecf20Sopenharmony_ci if (res->start & 0x0fffffff) { 2508c2ecf20Sopenharmony_ci ret = -EINVAL; 2518c2ecf20Sopenharmony_ci goto out_fb_release; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci info->apertures = alloc_apertures(1); 2558c2ecf20Sopenharmony_ci if (!info->apertures) { 2568c2ecf20Sopenharmony_ci ret = -ENOMEM; 2578c2ecf20Sopenharmony_ci goto out_fb_release; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci cfb->buffsize = resource_size(res); 2618c2ecf20Sopenharmony_ci info->fix.smem_start = res->start; 2628c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = info->fix.smem_start; 2638c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = cfb->buffsize; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci cfb->clk = devm_clk_get(dev, NULL); 2668c2ecf20Sopenharmony_ci if (IS_ERR(cfb->clk)) { 2678c2ecf20Sopenharmony_ci ret = PTR_ERR(cfb->clk); 2688c2ecf20Sopenharmony_ci goto out_fb_release; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci cfb->syscon = 2728c2ecf20Sopenharmony_ci syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); 2738c2ecf20Sopenharmony_ci if (IS_ERR(cfb->syscon)) { 2748c2ecf20Sopenharmony_ci ret = PTR_ERR(cfb->syscon); 2758c2ecf20Sopenharmony_ci goto out_fb_release; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci disp = of_parse_phandle(np, "display", 0); 2798c2ecf20Sopenharmony_ci if (!disp) { 2808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No display defined\n"); 2818c2ecf20Sopenharmony_ci ret = -ENODATA; 2828c2ecf20Sopenharmony_ci goto out_fb_release; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci ret = of_get_fb_videomode(disp, &cfb->mode, OF_USE_NATIVE_MODE); 2868c2ecf20Sopenharmony_ci if (ret) { 2878c2ecf20Sopenharmony_ci of_node_put(disp); 2888c2ecf20Sopenharmony_ci goto out_fb_release; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci of_property_read_u32(disp, "ac-prescale", &cfb->ac_prescale); 2928c2ecf20Sopenharmony_ci cfb->cmap_invert = of_property_read_bool(disp, "cmap-invert"); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = of_property_read_u32(disp, "bits-per-pixel", 2958c2ecf20Sopenharmony_ci &info->var.bits_per_pixel); 2968c2ecf20Sopenharmony_ci of_node_put(disp); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci goto out_fb_release; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Force disable LCD on any mismatch */ 3018c2ecf20Sopenharmony_ci if (info->fix.smem_start != (readb(cfb->base + CLPS711X_FBADDR) << 28)) 3028c2ecf20Sopenharmony_ci regmap_update_bits(cfb->syscon, SYSCON_OFFSET, 3038c2ecf20Sopenharmony_ci SYSCON1_LCDEN, 0); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = regmap_read(cfb->syscon, SYSCON_OFFSET, &val); 3068c2ecf20Sopenharmony_ci if (ret) 3078c2ecf20Sopenharmony_ci goto out_fb_release; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!(val & SYSCON1_LCDEN)) { 3108c2ecf20Sopenharmony_ci /* Setup start FB address */ 3118c2ecf20Sopenharmony_ci writeb(info->fix.smem_start >> 28, cfb->base + CLPS711X_FBADDR); 3128c2ecf20Sopenharmony_ci /* Clean FB memory */ 3138c2ecf20Sopenharmony_ci memset_io(info->screen_base, 0, cfb->buffsize); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci cfb->lcd_pwr = devm_regulator_get(dev, "lcd"); 3178c2ecf20Sopenharmony_ci if (PTR_ERR(cfb->lcd_pwr) == -EPROBE_DEFER) { 3188c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3198c2ecf20Sopenharmony_ci goto out_fb_release; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci info->fbops = &clps711x_fb_ops; 3238c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 3248c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; 3258c2ecf20Sopenharmony_ci info->var.height = -1; 3268c2ecf20Sopenharmony_ci info->var.width = -1; 3278c2ecf20Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 3288c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 3298c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 3308c2ecf20Sopenharmony_ci strlcpy(info->fix.id, CLPS711X_FB_NAME, sizeof(info->fix.id)); 3318c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, &cfb->mode); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, BIT(CLPS711X_FB_BPP_MAX), 0); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci goto out_fb_release; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = fb_set_var(info, &info->var); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci goto out_fb_dealloc_cmap; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci goto out_fb_dealloc_cmap; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci lcd = devm_lcd_device_register(dev, "clps711x-lcd", dev, cfb, 3468c2ecf20Sopenharmony_ci &clps711x_lcd_ops); 3478c2ecf20Sopenharmony_ci if (!IS_ERR(lcd)) 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = PTR_ERR(lcd); 3518c2ecf20Sopenharmony_ci unregister_framebuffer(info); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciout_fb_dealloc_cmap: 3548c2ecf20Sopenharmony_ci regmap_update_bits(cfb->syscon, SYSCON_OFFSET, SYSCON1_LCDEN, 0); 3558c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciout_fb_release: 3588c2ecf20Sopenharmony_ci framebuffer_release(info); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int clps711x_fb_remove(struct platform_device *pdev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 3668c2ecf20Sopenharmony_ci struct clps711x_fb_info *cfb = info->par; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci regmap_update_bits(cfb->syscon, SYSCON_OFFSET, SYSCON1_LCDEN, 0); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci unregister_framebuffer(info); 3718c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 3728c2ecf20Sopenharmony_ci framebuffer_release(info); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const struct of_device_id clps711x_fb_dt_ids[] = { 3788c2ecf20Sopenharmony_ci { .compatible = "cirrus,ep7209-fb", }, 3798c2ecf20Sopenharmony_ci { } 3808c2ecf20Sopenharmony_ci}; 3818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, clps711x_fb_dt_ids); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct platform_driver clps711x_fb_driver = { 3848c2ecf20Sopenharmony_ci .driver = { 3858c2ecf20Sopenharmony_ci .name = CLPS711X_FB_NAME, 3868c2ecf20Sopenharmony_ci .of_match_table = clps711x_fb_dt_ids, 3878c2ecf20Sopenharmony_ci }, 3888c2ecf20Sopenharmony_ci .probe = clps711x_fb_probe, 3898c2ecf20Sopenharmony_ci .remove = clps711x_fb_remove, 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_cimodule_platform_driver(clps711x_fb_driver); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 3948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic CLPS711X FB driver"); 3958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 396