18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Freescale i.MX Frame Buffer device driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Sascha Hauer, Pengutronix 58c2ecf20Sopenharmony_ci * Based on acornfb.c Copyright (C) Russell King. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 98c2ecf20Sopenharmony_ci * more details. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Please direct your questions and comments on this driver to the following 128c2ecf20Sopenharmony_ci * email address: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * linux-arm-kernel@lists.arm.linux.org.uk 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/mm.h> 248c2ecf20Sopenharmony_ci#include <linux/fb.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/init.h> 278c2ecf20Sopenharmony_ci#include <linux/ioport.h> 288c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 298c2ecf20Sopenharmony_ci#include <linux/clk.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 328c2ecf20Sopenharmony_ci#include <linux/io.h> 338c2ecf20Sopenharmony_ci#include <linux/lcd.h> 348c2ecf20Sopenharmony_ci#include <linux/math64.h> 358c2ecf20Sopenharmony_ci#include <linux/of.h> 368c2ecf20Sopenharmony_ci#include <linux/of_device.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <video/of_display_timing.h> 418c2ecf20Sopenharmony_ci#include <video/of_videomode.h> 428c2ecf20Sopenharmony_ci#include <video/videomode.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <linux/platform_data/video-imxfb.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * Complain if VAR is out of range. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci#define DEBUG_VAR 1 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define DRIVER_NAME "imx-fb" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define LCDC_SSA 0x00 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define LCDC_SIZE 0x04 568c2ecf20Sopenharmony_ci#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define YMAX_MASK_IMX1 0x1ff 598c2ecf20Sopenharmony_ci#define YMAX_MASK_IMX21 0x3ff 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define LCDC_VPW 0x08 628c2ecf20Sopenharmony_ci#define VPW_VPW(x) ((x) & 0x3ff) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define LCDC_CPOS 0x0C 658c2ecf20Sopenharmony_ci#define CPOS_CC1 (1<<31) 668c2ecf20Sopenharmony_ci#define CPOS_CC0 (1<<30) 678c2ecf20Sopenharmony_ci#define CPOS_OP (1<<28) 688c2ecf20Sopenharmony_ci#define CPOS_CXP(x) (((x) & 3ff) << 16) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define LCDC_LCWHB 0x10 718c2ecf20Sopenharmony_ci#define LCWHB_BK_EN (1<<31) 728c2ecf20Sopenharmony_ci#define LCWHB_CW(w) (((w) & 0x1f) << 24) 738c2ecf20Sopenharmony_ci#define LCWHB_CH(h) (((h) & 0x1f) << 16) 748c2ecf20Sopenharmony_ci#define LCWHB_BD(x) ((x) & 0xff) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define LCDC_LCHCC 0x14 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define LCDC_PCR 0x18 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define LCDC_HCR 0x1C 818c2ecf20Sopenharmony_ci#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26) 828c2ecf20Sopenharmony_ci#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8) 838c2ecf20Sopenharmony_ci#define HCR_H_WAIT_2(x) ((x) & 0xff) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define LCDC_VCR 0x20 868c2ecf20Sopenharmony_ci#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26) 878c2ecf20Sopenharmony_ci#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8) 888c2ecf20Sopenharmony_ci#define VCR_V_WAIT_2(x) ((x) & 0xff) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define LCDC_POS 0x24 918c2ecf20Sopenharmony_ci#define POS_POS(x) ((x) & 1f) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define LCDC_LSCR1 0x28 948c2ecf20Sopenharmony_ci/* bit fields in imxfb.h */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define LCDC_PWMR 0x2C 978c2ecf20Sopenharmony_ci/* bit fields in imxfb.h */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define LCDC_DMACR 0x30 1008c2ecf20Sopenharmony_ci/* bit fields in imxfb.h */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define LCDC_RMCR 0x34 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define RMCR_LCDC_EN_MX1 (1<<1) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define RMCR_SELF_REF (1<<0) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define LCDC_LCDICR 0x38 1098c2ecf20Sopenharmony_ci#define LCDICR_INT_SYN (1<<2) 1108c2ecf20Sopenharmony_ci#define LCDICR_INT_CON (1) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define LCDC_LCDISR 0x40 1138c2ecf20Sopenharmony_ci#define LCDISR_UDR_ERR (1<<3) 1148c2ecf20Sopenharmony_ci#define LCDISR_ERR_RES (1<<2) 1158c2ecf20Sopenharmony_ci#define LCDISR_EOF (1<<1) 1168c2ecf20Sopenharmony_ci#define LCDISR_BOF (1<<0) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define IMXFB_LSCR1_DEFAULT 0x00120300 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define LCDC_LAUSCR 0x80 1218c2ecf20Sopenharmony_ci#define LAUSCR_AUS_MODE (1<<31) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Used fb-mode. Can be set on kernel command line, therefore file-static. */ 1248c2ecf20Sopenharmony_cistatic const char *fb_mode; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * These are the bitfields for each 1288c2ecf20Sopenharmony_ci * display depth that we support. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_cistruct imxfb_rgb { 1318c2ecf20Sopenharmony_ci struct fb_bitfield red; 1328c2ecf20Sopenharmony_ci struct fb_bitfield green; 1338c2ecf20Sopenharmony_ci struct fb_bitfield blue; 1348c2ecf20Sopenharmony_ci struct fb_bitfield transp; 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cienum imxfb_type { 1388c2ecf20Sopenharmony_ci IMX1_FB, 1398c2ecf20Sopenharmony_ci IMX21_FB, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistruct imxfb_info { 1438c2ecf20Sopenharmony_ci struct platform_device *pdev; 1448c2ecf20Sopenharmony_ci void __iomem *regs; 1458c2ecf20Sopenharmony_ci struct clk *clk_ipg; 1468c2ecf20Sopenharmony_ci struct clk *clk_ahb; 1478c2ecf20Sopenharmony_ci struct clk *clk_per; 1488c2ecf20Sopenharmony_ci enum imxfb_type devtype; 1498c2ecf20Sopenharmony_ci bool enabled; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * These are the addresses we mapped 1538c2ecf20Sopenharmony_ci * the framebuffer memory region to. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci dma_addr_t map_dma; 1568c2ecf20Sopenharmony_ci u_int map_size; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci u_int palette_size; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci dma_addr_t dbar1; 1618c2ecf20Sopenharmony_ci dma_addr_t dbar2; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci u_int pcr; 1648c2ecf20Sopenharmony_ci u_int lauscr; 1658c2ecf20Sopenharmony_ci u_int pwmr; 1668c2ecf20Sopenharmony_ci u_int lscr1; 1678c2ecf20Sopenharmony_ci u_int dmacr; 1688c2ecf20Sopenharmony_ci bool cmap_inverse; 1698c2ecf20Sopenharmony_ci bool cmap_static; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci struct imx_fb_videomode *mode; 1728c2ecf20Sopenharmony_ci int num_modes; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci struct regulator *lcd_pwr; 1758c2ecf20Sopenharmony_ci int lcd_pwr_enabled; 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct platform_device_id imxfb_devtype[] = { 1798c2ecf20Sopenharmony_ci { 1808c2ecf20Sopenharmony_ci .name = "imx1-fb", 1818c2ecf20Sopenharmony_ci .driver_data = IMX1_FB, 1828c2ecf20Sopenharmony_ci }, { 1838c2ecf20Sopenharmony_ci .name = "imx21-fb", 1848c2ecf20Sopenharmony_ci .driver_data = IMX21_FB, 1858c2ecf20Sopenharmony_ci }, { 1868c2ecf20Sopenharmony_ci /* sentinel */ 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, imxfb_devtype); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct of_device_id imxfb_of_dev_id[] = { 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci .compatible = "fsl,imx1-fb", 1948c2ecf20Sopenharmony_ci .data = &imxfb_devtype[IMX1_FB], 1958c2ecf20Sopenharmony_ci }, { 1968c2ecf20Sopenharmony_ci .compatible = "fsl,imx21-fb", 1978c2ecf20Sopenharmony_ci .data = &imxfb_devtype[IMX21_FB], 1988c2ecf20Sopenharmony_ci }, { 1998c2ecf20Sopenharmony_ci /* sentinel */ 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, imxfb_of_dev_id); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic inline int is_imx1_fb(struct imxfb_info *fbi) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return fbi->devtype == IMX1_FB; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define IMX_NAME "IMX" 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * Minimum X and Y resolutions 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci#define MIN_XRES 64 2158c2ecf20Sopenharmony_ci#define MIN_YRES 64 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* Actually this really is 18bit support, the lowest 2 bits of each colour 2188c2ecf20Sopenharmony_ci * are unused in hardware. We claim to have 24bit support to make software 2198c2ecf20Sopenharmony_ci * like X work, which does not support 18bit. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic struct imxfb_rgb def_rgb_18 = { 2228c2ecf20Sopenharmony_ci .red = {.offset = 16, .length = 8,}, 2238c2ecf20Sopenharmony_ci .green = {.offset = 8, .length = 8,}, 2248c2ecf20Sopenharmony_ci .blue = {.offset = 0, .length = 8,}, 2258c2ecf20Sopenharmony_ci .transp = {.offset = 0, .length = 0,}, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic struct imxfb_rgb def_rgb_16_tft = { 2298c2ecf20Sopenharmony_ci .red = {.offset = 11, .length = 5,}, 2308c2ecf20Sopenharmony_ci .green = {.offset = 5, .length = 6,}, 2318c2ecf20Sopenharmony_ci .blue = {.offset = 0, .length = 5,}, 2328c2ecf20Sopenharmony_ci .transp = {.offset = 0, .length = 0,}, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct imxfb_rgb def_rgb_16_stn = { 2368c2ecf20Sopenharmony_ci .red = {.offset = 8, .length = 4,}, 2378c2ecf20Sopenharmony_ci .green = {.offset = 4, .length = 4,}, 2388c2ecf20Sopenharmony_ci .blue = {.offset = 0, .length = 4,}, 2398c2ecf20Sopenharmony_ci .transp = {.offset = 0, .length = 0,}, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct imxfb_rgb def_rgb_8 = { 2438c2ecf20Sopenharmony_ci .red = {.offset = 0, .length = 8,}, 2448c2ecf20Sopenharmony_ci .green = {.offset = 0, .length = 8,}, 2458c2ecf20Sopenharmony_ci .blue = {.offset = 0, .length = 8,}, 2468c2ecf20Sopenharmony_ci .transp = {.offset = 0, .length = 0,}, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int imxfb_activate_var(struct fb_var_screeninfo *var, 2508c2ecf20Sopenharmony_ci struct fb_info *info); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci chan &= 0xffff; 2558c2ecf20Sopenharmony_ci chan >>= 16 - bf->length; 2568c2ecf20Sopenharmony_ci return chan << bf->offset; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 2608c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 2638c2ecf20Sopenharmony_ci u_int val, ret = 1; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) 2668c2ecf20Sopenharmony_ci if (regno < fbi->palette_size) { 2678c2ecf20Sopenharmony_ci val = (CNVT_TOHW(red, 4) << 8) | 2688c2ecf20Sopenharmony_ci (CNVT_TOHW(green,4) << 4) | 2698c2ecf20Sopenharmony_ci CNVT_TOHW(blue, 4); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci writel(val, fbi->regs + 0x800 + (regno << 2)); 2728c2ecf20Sopenharmony_ci ret = 0; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 2788c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 2818c2ecf20Sopenharmony_ci unsigned int val; 2828c2ecf20Sopenharmony_ci int ret = 1; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * If inverse mode was selected, invert all the colours 2868c2ecf20Sopenharmony_ci * rather than the register number. The register number 2878c2ecf20Sopenharmony_ci * is what you poke into the framebuffer to produce the 2888c2ecf20Sopenharmony_ci * colour you requested. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci if (fbi->cmap_inverse) { 2918c2ecf20Sopenharmony_ci red = 0xffff - red; 2928c2ecf20Sopenharmony_ci green = 0xffff - green; 2938c2ecf20Sopenharmony_ci blue = 0xffff - blue; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * If greyscale is true, then we convert the RGB value 2988c2ecf20Sopenharmony_ci * to greyscale no mater what visual we are using. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci if (info->var.grayscale) 3018c2ecf20Sopenharmony_ci red = green = blue = (19595 * red + 38470 * green + 3028c2ecf20Sopenharmony_ci 7471 * blue) >> 16; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci switch (info->fix.visual) { 3058c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 3068c2ecf20Sopenharmony_ci /* 3078c2ecf20Sopenharmony_ci * 12 or 16-bit True Colour. We encode the RGB value 3088c2ecf20Sopenharmony_ci * according to the RGB bitfield information. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci if (regno < 16) { 3118c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci val = chan_to_field(red, &info->var.red); 3148c2ecf20Sopenharmony_ci val |= chan_to_field(green, &info->var.green); 3158c2ecf20Sopenharmony_ci val |= chan_to_field(blue, &info->var.blue); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pal[regno] = val; 3188c2ecf20Sopenharmony_ci ret = 0; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci case FB_VISUAL_STATIC_PSEUDOCOLOR: 3238c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 3248c2ecf20Sopenharmony_ci ret = imxfb_setpalettereg(regno, red, green, blue, trans, info); 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct imx_fb_videomode *m; 3348c2ecf20Sopenharmony_ci int i; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!fb_mode) 3378c2ecf20Sopenharmony_ci return &fbi->mode[0]; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) { 3408c2ecf20Sopenharmony_ci if (!strcmp(m->mode.name, fb_mode)) 3418c2ecf20Sopenharmony_ci return m; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci return NULL; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* 3478c2ecf20Sopenharmony_ci * imxfb_check_var(): 3488c2ecf20Sopenharmony_ci * Round up in the following order: bits_per_pixel, xres, 3498c2ecf20Sopenharmony_ci * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 3508c2ecf20Sopenharmony_ci * bitfields, horizontal timing, vertical timing. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_cistatic int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 3558c2ecf20Sopenharmony_ci struct imxfb_rgb *rgb; 3568c2ecf20Sopenharmony_ci const struct imx_fb_videomode *imxfb_mode; 3578c2ecf20Sopenharmony_ci unsigned long lcd_clk; 3588c2ecf20Sopenharmony_ci unsigned long long tmp; 3598c2ecf20Sopenharmony_ci u32 pcr = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (var->xres < MIN_XRES) 3628c2ecf20Sopenharmony_ci var->xres = MIN_XRES; 3638c2ecf20Sopenharmony_ci if (var->yres < MIN_YRES) 3648c2ecf20Sopenharmony_ci var->yres = MIN_YRES; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci imxfb_mode = imxfb_find_mode(fbi); 3678c2ecf20Sopenharmony_ci if (!imxfb_mode) 3688c2ecf20Sopenharmony_ci return -EINVAL; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci var->xres = imxfb_mode->mode.xres; 3718c2ecf20Sopenharmony_ci var->yres = imxfb_mode->mode.yres; 3728c2ecf20Sopenharmony_ci var->bits_per_pixel = imxfb_mode->bpp; 3738c2ecf20Sopenharmony_ci var->pixclock = imxfb_mode->mode.pixclock; 3748c2ecf20Sopenharmony_ci var->hsync_len = imxfb_mode->mode.hsync_len; 3758c2ecf20Sopenharmony_ci var->left_margin = imxfb_mode->mode.left_margin; 3768c2ecf20Sopenharmony_ci var->right_margin = imxfb_mode->mode.right_margin; 3778c2ecf20Sopenharmony_ci var->vsync_len = imxfb_mode->mode.vsync_len; 3788c2ecf20Sopenharmony_ci var->upper_margin = imxfb_mode->mode.upper_margin; 3798c2ecf20Sopenharmony_ci var->lower_margin = imxfb_mode->mode.lower_margin; 3808c2ecf20Sopenharmony_ci var->sync = imxfb_mode->mode.sync; 3818c2ecf20Sopenharmony_ci var->xres_virtual = max(var->xres_virtual, var->xres); 3828c2ecf20Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci lcd_clk = clk_get_rate(fbi->clk_per); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci tmp = var->pixclock * (unsigned long long)lcd_clk; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci do_div(tmp, 1000000); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (do_div(tmp, 1000000) > 500000) 3938c2ecf20Sopenharmony_ci tmp++; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci pcr = (unsigned int)tmp; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (--pcr > 0x3F) { 3988c2ecf20Sopenharmony_ci pcr = 0x3F; 3998c2ecf20Sopenharmony_ci printk(KERN_WARNING "Must limit pixel clock to %luHz\n", 4008c2ecf20Sopenharmony_ci lcd_clk / pcr); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 4048c2ecf20Sopenharmony_ci case 32: 4058c2ecf20Sopenharmony_ci pcr |= PCR_BPIX_18; 4068c2ecf20Sopenharmony_ci rgb = &def_rgb_18; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case 16: 4098c2ecf20Sopenharmony_ci default: 4108c2ecf20Sopenharmony_ci if (is_imx1_fb(fbi)) 4118c2ecf20Sopenharmony_ci pcr |= PCR_BPIX_12; 4128c2ecf20Sopenharmony_ci else 4138c2ecf20Sopenharmony_ci pcr |= PCR_BPIX_16; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (imxfb_mode->pcr & PCR_TFT) 4168c2ecf20Sopenharmony_ci rgb = &def_rgb_16_tft; 4178c2ecf20Sopenharmony_ci else 4188c2ecf20Sopenharmony_ci rgb = &def_rgb_16_stn; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case 8: 4218c2ecf20Sopenharmony_ci pcr |= PCR_BPIX_8; 4228c2ecf20Sopenharmony_ci rgb = &def_rgb_8; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* add sync polarities */ 4278c2ecf20Sopenharmony_ci pcr |= imxfb_mode->pcr & ~(0x3f | (7 << 25)); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci fbi->pcr = pcr; 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * The LCDC AUS Mode Control Register does not exist on imx1. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci if (!is_imx1_fb(fbi) && imxfb_mode->aus_mode) 4348c2ecf20Sopenharmony_ci fbi->lauscr = LAUSCR_AUS_MODE; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * Copy the RGB parameters for this display 4388c2ecf20Sopenharmony_ci * from the machine specific parameters. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci var->red = rgb->red; 4418c2ecf20Sopenharmony_ci var->green = rgb->green; 4428c2ecf20Sopenharmony_ci var->blue = rgb->blue; 4438c2ecf20Sopenharmony_ci var->transp = rgb->transp; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci pr_debug("RGBT length = %d:%d:%d:%d\n", 4468c2ecf20Sopenharmony_ci var->red.length, var->green.length, var->blue.length, 4478c2ecf20Sopenharmony_ci var->transp.length); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci pr_debug("RGBT offset = %d:%d:%d:%d\n", 4508c2ecf20Sopenharmony_ci var->red.offset, var->green.offset, var->blue.offset, 4518c2ecf20Sopenharmony_ci var->transp.offset); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * imxfb_set_par(): 4588c2ecf20Sopenharmony_ci * Set the user defined part of the display for the specified console 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_cistatic int imxfb_set_par(struct fb_info *info) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 4638c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32) 4668c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 4678c2ecf20Sopenharmony_ci else if (!fbi->cmap_static) 4688c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 4698c2ecf20Sopenharmony_ci else { 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Some people have weird ideas about wanting static 4728c2ecf20Sopenharmony_ci * pseudocolor maps. I suspect their user space 4738c2ecf20Sopenharmony_ci * applications are broken. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; 4798c2ecf20Sopenharmony_ci fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci imxfb_activate_var(var, info); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int imxfb_enable_controller(struct imxfb_info *fbi) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci int ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (fbi->enabled) 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci pr_debug("Enabling LCD controller\n"); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci writel(fbi->map_dma, fbi->regs + LCDC_SSA); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* panning offset 0 (0 pixel offset) */ 4988c2ecf20Sopenharmony_ci writel(0x00000000, fbi->regs + LCDC_POS); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* disable hardware cursor */ 5018c2ecf20Sopenharmony_ci writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1), 5028c2ecf20Sopenharmony_ci fbi->regs + LCDC_CPOS); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * RMCR_LCDC_EN_MX1 is present on i.MX1 only, but doesn't hurt 5068c2ecf20Sopenharmony_ci * on other SoCs 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fbi->clk_ipg); 5118c2ecf20Sopenharmony_ci if (ret) 5128c2ecf20Sopenharmony_ci goto err_enable_ipg; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fbi->clk_ahb); 5158c2ecf20Sopenharmony_ci if (ret) 5168c2ecf20Sopenharmony_ci goto err_enable_ahb; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fbi->clk_per); 5198c2ecf20Sopenharmony_ci if (ret) 5208c2ecf20Sopenharmony_ci goto err_enable_per; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci fbi->enabled = true; 5238c2ecf20Sopenharmony_ci return 0; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cierr_enable_per: 5268c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_ahb); 5278c2ecf20Sopenharmony_cierr_enable_ahb: 5288c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_ipg); 5298c2ecf20Sopenharmony_cierr_enable_ipg: 5308c2ecf20Sopenharmony_ci writel(0, fbi->regs + LCDC_RMCR); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void imxfb_disable_controller(struct imxfb_info *fbi) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci if (!fbi->enabled) 5388c2ecf20Sopenharmony_ci return; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci pr_debug("Disabling LCD controller\n"); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_per); 5438c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_ahb); 5448c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_ipg); 5458c2ecf20Sopenharmony_ci fbi->enabled = false; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci writel(0, fbi->regs + LCDC_RMCR); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int imxfb_blank(int blank, struct fb_info *info) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci pr_debug("imxfb_blank: blank=%d\n", blank); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci switch (blank) { 5578c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 5588c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 5598c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 5608c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 5618c2ecf20Sopenharmony_ci imxfb_disable_controller(fbi); 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 5658c2ecf20Sopenharmony_ci return imxfb_enable_controller(fbi); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic const struct fb_ops imxfb_ops = { 5718c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5728c2ecf20Sopenharmony_ci .fb_check_var = imxfb_check_var, 5738c2ecf20Sopenharmony_ci .fb_set_par = imxfb_set_par, 5748c2ecf20Sopenharmony_ci .fb_setcolreg = imxfb_setcolreg, 5758c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 5768c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 5778c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5788c2ecf20Sopenharmony_ci .fb_blank = imxfb_blank, 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/* 5828c2ecf20Sopenharmony_ci * imxfb_activate_var(): 5838c2ecf20Sopenharmony_ci * Configures LCD Controller based on entries in var parameter. Settings are 5848c2ecf20Sopenharmony_ci * only written to the controller if changes were made. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_cistatic int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 5898c2ecf20Sopenharmony_ci u32 ymax_mask = is_imx1_fb(fbi) ? YMAX_MASK_IMX1 : YMAX_MASK_IMX21; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", 5928c2ecf20Sopenharmony_ci var->xres, var->hsync_len, 5938c2ecf20Sopenharmony_ci var->left_margin, var->right_margin); 5948c2ecf20Sopenharmony_ci pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n", 5958c2ecf20Sopenharmony_ci var->yres, var->vsync_len, 5968c2ecf20Sopenharmony_ci var->upper_margin, var->lower_margin); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci#if DEBUG_VAR 5998c2ecf20Sopenharmony_ci if (var->xres < 16 || var->xres > 1024) 6008c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid xres %d\n", 6018c2ecf20Sopenharmony_ci info->fix.id, var->xres); 6028c2ecf20Sopenharmony_ci if (var->hsync_len < 1 || var->hsync_len > 64) 6038c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid hsync_len %d\n", 6048c2ecf20Sopenharmony_ci info->fix.id, var->hsync_len); 6058c2ecf20Sopenharmony_ci if (var->left_margin < 3 || var->left_margin > 255) 6068c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid left_margin %d\n", 6078c2ecf20Sopenharmony_ci info->fix.id, var->left_margin); 6088c2ecf20Sopenharmony_ci if (var->right_margin < 1 || var->right_margin > 255) 6098c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid right_margin %d\n", 6108c2ecf20Sopenharmony_ci info->fix.id, var->right_margin); 6118c2ecf20Sopenharmony_ci if (var->yres < 1 || var->yres > ymax_mask) 6128c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid yres %d\n", 6138c2ecf20Sopenharmony_ci info->fix.id, var->yres); 6148c2ecf20Sopenharmony_ci if (var->vsync_len > 100) 6158c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid vsync_len %d\n", 6168c2ecf20Sopenharmony_ci info->fix.id, var->vsync_len); 6178c2ecf20Sopenharmony_ci if (var->upper_margin > 63) 6188c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid upper_margin %d\n", 6198c2ecf20Sopenharmony_ci info->fix.id, var->upper_margin); 6208c2ecf20Sopenharmony_ci if (var->lower_margin > 255) 6218c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: invalid lower_margin %d\n", 6228c2ecf20Sopenharmony_ci info->fix.id, var->lower_margin); 6238c2ecf20Sopenharmony_ci#endif 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* physical screen start address */ 6268c2ecf20Sopenharmony_ci writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4), 6278c2ecf20Sopenharmony_ci fbi->regs + LCDC_VPW); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci writel(HCR_H_WIDTH(var->hsync_len - 1) | 6308c2ecf20Sopenharmony_ci HCR_H_WAIT_1(var->right_margin - 1) | 6318c2ecf20Sopenharmony_ci HCR_H_WAIT_2(var->left_margin - 3), 6328c2ecf20Sopenharmony_ci fbi->regs + LCDC_HCR); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci writel(VCR_V_WIDTH(var->vsync_len) | 6358c2ecf20Sopenharmony_ci VCR_V_WAIT_1(var->lower_margin) | 6368c2ecf20Sopenharmony_ci VCR_V_WAIT_2(var->upper_margin), 6378c2ecf20Sopenharmony_ci fbi->regs + LCDC_VCR); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci writel(SIZE_XMAX(var->xres) | (var->yres & ymax_mask), 6408c2ecf20Sopenharmony_ci fbi->regs + LCDC_SIZE); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci writel(fbi->pcr, fbi->regs + LCDC_PCR); 6438c2ecf20Sopenharmony_ci if (fbi->pwmr) 6448c2ecf20Sopenharmony_ci writel(fbi->pwmr, fbi->regs + LCDC_PWMR); 6458c2ecf20Sopenharmony_ci writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* dmacr = 0 is no valid value, as we need DMA control marks. */ 6488c2ecf20Sopenharmony_ci if (fbi->dmacr) 6498c2ecf20Sopenharmony_ci writel(fbi->dmacr, fbi->regs + LCDC_DMACR); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (fbi->lauscr) 6528c2ecf20Sopenharmony_ci writel(fbi->lauscr, fbi->regs + LCDC_LAUSCR); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int imxfb_init_fbinfo(struct platform_device *pdev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct imx_fb_platform_data *pdata = dev_get_platdata(&pdev->dev); 6608c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(&pdev->dev); 6618c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 6628c2ecf20Sopenharmony_ci struct device_node *np; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci pr_debug("%s\n",__func__); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci info->pseudo_palette = kmalloc_array(16, sizeof(u32), GFP_KERNEL); 6678c2ecf20Sopenharmony_ci if (!info->pseudo_palette) 6688c2ecf20Sopenharmony_ci return -ENOMEM; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci memset(fbi, 0, sizeof(struct imxfb_info)); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci fbi->devtype = pdev->id_entry->driver_data; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id)); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 6778c2ecf20Sopenharmony_ci info->fix.type_aux = 0; 6788c2ecf20Sopenharmony_ci info->fix.xpanstep = 0; 6798c2ecf20Sopenharmony_ci info->fix.ypanstep = 0; 6808c2ecf20Sopenharmony_ci info->fix.ywrapstep = 0; 6818c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci info->var.nonstd = 0; 6848c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 6858c2ecf20Sopenharmony_ci info->var.height = -1; 6868c2ecf20Sopenharmony_ci info->var.width = -1; 6878c2ecf20Sopenharmony_ci info->var.accel_flags = 0; 6888c2ecf20Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci info->fbops = &imxfb_ops; 6918c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT | 6928c2ecf20Sopenharmony_ci FBINFO_READS_FAST; 6938c2ecf20Sopenharmony_ci if (pdata) { 6948c2ecf20Sopenharmony_ci fbi->lscr1 = pdata->lscr1; 6958c2ecf20Sopenharmony_ci fbi->dmacr = pdata->dmacr; 6968c2ecf20Sopenharmony_ci fbi->pwmr = pdata->pwmr; 6978c2ecf20Sopenharmony_ci } else { 6988c2ecf20Sopenharmony_ci np = pdev->dev.of_node; 6998c2ecf20Sopenharmony_ci info->var.grayscale = of_property_read_bool(np, 7008c2ecf20Sopenharmony_ci "cmap-greyscale"); 7018c2ecf20Sopenharmony_ci fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse"); 7028c2ecf20Sopenharmony_ci fbi->cmap_static = of_property_read_bool(np, "cmap-static"); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci fbi->lscr1 = IMXFB_LSCR1_DEFAULT; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,lpccr", &fbi->pwmr); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int imxfb_of_read_mode(struct device *dev, struct device_node *np, 7178c2ecf20Sopenharmony_ci struct imx_fb_videomode *imxfb_mode) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci int ret; 7208c2ecf20Sopenharmony_ci struct fb_videomode *of_mode = &imxfb_mode->mode; 7218c2ecf20Sopenharmony_ci u32 bpp; 7228c2ecf20Sopenharmony_ci u32 pcr; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci ret = of_property_read_string(np, "model", &of_mode->name); 7258c2ecf20Sopenharmony_ci if (ret) 7268c2ecf20Sopenharmony_ci of_mode->name = NULL; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE); 7298c2ecf20Sopenharmony_ci if (ret) { 7308c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get videomode from DT\n"); 7318c2ecf20Sopenharmony_ci return ret; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "bits-per-pixel", &bpp); 7358c2ecf20Sopenharmony_ci ret |= of_property_read_u32(np, "fsl,pcr", &pcr); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (ret) { 7388c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read bpp and pcr from DT\n"); 7398c2ecf20Sopenharmony_ci return -EINVAL; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (bpp < 1 || bpp > 255) { 7438c2ecf20Sopenharmony_ci dev_err(dev, "Bits per pixel have to be between 1 and 255\n"); 7448c2ecf20Sopenharmony_ci return -EINVAL; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci imxfb_mode->bpp = bpp; 7488c2ecf20Sopenharmony_ci imxfb_mode->pcr = pcr; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* 7518c2ecf20Sopenharmony_ci * fsl,aus-mode is optional 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ci imxfb_mode->aus_mode = of_property_read_bool(np, "fsl,aus-mode"); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return 0; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int imxfb_lcd_check_fb(struct lcd_device *lcddev, struct fb_info *fi) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (!fi || fi->par == fbi) 7638c2ecf20Sopenharmony_ci return 1; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic int imxfb_lcd_get_contrast(struct lcd_device *lcddev) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return fbi->pwmr & 0xff; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int imxfb_lcd_set_contrast(struct lcd_device *lcddev, int contrast) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (fbi->pwmr && fbi->enabled) { 7808c2ecf20Sopenharmony_ci if (contrast > 255) 7818c2ecf20Sopenharmony_ci contrast = 255; 7828c2ecf20Sopenharmony_ci else if (contrast < 0) 7838c2ecf20Sopenharmony_ci contrast = 0; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci fbi->pwmr &= ~0xff; 7868c2ecf20Sopenharmony_ci fbi->pwmr |= contrast; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci writel(fbi->pwmr, fbi->regs + LCDC_PWMR); 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic int imxfb_lcd_get_power(struct lcd_device *lcddev) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (!IS_ERR(fbi->lcd_pwr) && 7998c2ecf20Sopenharmony_ci !regulator_is_enabled(fbi->lcd_pwr)) 8008c2ecf20Sopenharmony_ci return FB_BLANK_POWERDOWN; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci return FB_BLANK_UNBLANK; 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic int imxfb_regulator_set(struct imxfb_info *fbi, int enable) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci int ret; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (enable == fbi->lcd_pwr_enabled) 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (enable) 8138c2ecf20Sopenharmony_ci ret = regulator_enable(fbi->lcd_pwr); 8148c2ecf20Sopenharmony_ci else 8158c2ecf20Sopenharmony_ci ret = regulator_disable(fbi->lcd_pwr); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (ret == 0) 8188c2ecf20Sopenharmony_ci fbi->lcd_pwr_enabled = enable; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return ret; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int imxfb_lcd_set_power(struct lcd_device *lcddev, int power) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct imxfb_info *fbi = dev_get_drvdata(&lcddev->dev); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!IS_ERR(fbi->lcd_pwr)) 8288c2ecf20Sopenharmony_ci return imxfb_regulator_set(fbi, power == FB_BLANK_UNBLANK); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic struct lcd_ops imxfb_lcd_ops = { 8348c2ecf20Sopenharmony_ci .check_fb = imxfb_lcd_check_fb, 8358c2ecf20Sopenharmony_ci .get_contrast = imxfb_lcd_get_contrast, 8368c2ecf20Sopenharmony_ci .set_contrast = imxfb_lcd_set_contrast, 8378c2ecf20Sopenharmony_ci .get_power = imxfb_lcd_get_power, 8388c2ecf20Sopenharmony_ci .set_power = imxfb_lcd_set_power, 8398c2ecf20Sopenharmony_ci}; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int imxfb_setup(void) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci char *opt, *options = NULL; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (fb_get_options("imxfb", &options)) 8468c2ecf20Sopenharmony_ci return -ENODEV; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (!options || !*options) 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci while ((opt = strsep(&options, ",")) != NULL) { 8528c2ecf20Sopenharmony_ci if (!*opt) 8538c2ecf20Sopenharmony_ci continue; 8548c2ecf20Sopenharmony_ci else 8558c2ecf20Sopenharmony_ci fb_mode = opt; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic int imxfb_probe(struct platform_device *pdev) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct imxfb_info *fbi; 8648c2ecf20Sopenharmony_ci struct lcd_device *lcd; 8658c2ecf20Sopenharmony_ci struct fb_info *info; 8668c2ecf20Sopenharmony_ci struct imx_fb_platform_data *pdata; 8678c2ecf20Sopenharmony_ci struct resource *res; 8688c2ecf20Sopenharmony_ci struct imx_fb_videomode *m; 8698c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 8708c2ecf20Sopenharmony_ci int ret, i; 8718c2ecf20Sopenharmony_ci int bytes_per_pixel; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "i.MX Framebuffer driver\n"); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci ret = imxfb_setup(); 8768c2ecf20Sopenharmony_ci if (ret < 0) 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci of_id = of_match_device(imxfb_of_dev_id, &pdev->dev); 8808c2ecf20Sopenharmony_ci if (of_id) 8818c2ecf20Sopenharmony_ci pdev->id_entry = of_id->data; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8848c2ecf20Sopenharmony_ci if (!res) 8858c2ecf20Sopenharmony_ci return -ENODEV; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev); 8908c2ecf20Sopenharmony_ci if (!info) 8918c2ecf20Sopenharmony_ci return -ENOMEM; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci fbi = info->par; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci ret = imxfb_init_fbinfo(pdev); 8988c2ecf20Sopenharmony_ci if (ret < 0) 8998c2ecf20Sopenharmony_ci goto failed_init; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (pdata) { 9028c2ecf20Sopenharmony_ci if (!fb_mode) 9038c2ecf20Sopenharmony_ci fb_mode = pdata->mode[0].mode.name; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci fbi->mode = pdata->mode; 9068c2ecf20Sopenharmony_ci fbi->num_modes = pdata->num_modes; 9078c2ecf20Sopenharmony_ci } else { 9088c2ecf20Sopenharmony_ci struct device_node *display_np; 9098c2ecf20Sopenharmony_ci fb_mode = NULL; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci display_np = of_parse_phandle(pdev->dev.of_node, "display", 0); 9128c2ecf20Sopenharmony_ci if (!display_np) { 9138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No display defined in devicetree\n"); 9148c2ecf20Sopenharmony_ci ret = -EINVAL; 9158c2ecf20Sopenharmony_ci goto failed_of_parse; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* 9198c2ecf20Sopenharmony_ci * imxfb does not support more modes, we choose only the native 9208c2ecf20Sopenharmony_ci * mode. 9218c2ecf20Sopenharmony_ci */ 9228c2ecf20Sopenharmony_ci fbi->num_modes = 1; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci fbi->mode = devm_kzalloc(&pdev->dev, 9258c2ecf20Sopenharmony_ci sizeof(struct imx_fb_videomode), GFP_KERNEL); 9268c2ecf20Sopenharmony_ci if (!fbi->mode) { 9278c2ecf20Sopenharmony_ci ret = -ENOMEM; 9288c2ecf20Sopenharmony_ci goto failed_of_parse; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode); 9328c2ecf20Sopenharmony_ci if (ret) 9338c2ecf20Sopenharmony_ci goto failed_of_parse; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci /* Calculate maximum bytes used per pixel. In most cases this should 9378c2ecf20Sopenharmony_ci * be the same as m->bpp/8 */ 9388c2ecf20Sopenharmony_ci m = &fbi->mode[0]; 9398c2ecf20Sopenharmony_ci bytes_per_pixel = (m->bpp + 7) / 8; 9408c2ecf20Sopenharmony_ci for (i = 0; i < fbi->num_modes; i++, m++) 9418c2ecf20Sopenharmony_ci info->fix.smem_len = max_t(size_t, info->fix.smem_len, 9428c2ecf20Sopenharmony_ci m->mode.xres * m->mode.yres * bytes_per_pixel); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci res = request_mem_region(res->start, resource_size(res), 9458c2ecf20Sopenharmony_ci DRIVER_NAME); 9468c2ecf20Sopenharmony_ci if (!res) { 9478c2ecf20Sopenharmony_ci ret = -EBUSY; 9488c2ecf20Sopenharmony_ci goto failed_req; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci fbi->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 9528c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk_ipg)) { 9538c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi->clk_ipg); 9548c2ecf20Sopenharmony_ci goto failed_getclock; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci * The LCDC controller does not have an enable bit. The 9598c2ecf20Sopenharmony_ci * controller starts directly when the clocks are enabled. 9608c2ecf20Sopenharmony_ci * If the clocks are enabled when the controller is not yet 9618c2ecf20Sopenharmony_ci * programmed with proper register values (enabled at the 9628c2ecf20Sopenharmony_ci * bootloader, for example) then it just goes into some undefined 9638c2ecf20Sopenharmony_ci * state. 9648c2ecf20Sopenharmony_ci * To avoid this issue, let's enable and disable LCDC IPG clock 9658c2ecf20Sopenharmony_ci * so that we force some kind of 'reset' to the LCDC block. 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fbi->clk_ipg); 9688c2ecf20Sopenharmony_ci if (ret) 9698c2ecf20Sopenharmony_ci goto failed_getclock; 9708c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk_ipg); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); 9738c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk_ahb)) { 9748c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi->clk_ahb); 9758c2ecf20Sopenharmony_ci goto failed_getclock; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci fbi->clk_per = devm_clk_get(&pdev->dev, "per"); 9798c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk_per)) { 9808c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi->clk_per); 9818c2ecf20Sopenharmony_ci goto failed_getclock; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci fbi->regs = ioremap(res->start, resource_size(res)); 9858c2ecf20Sopenharmony_ci if (fbi->regs == NULL) { 9868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map frame buffer registers\n"); 9878c2ecf20Sopenharmony_ci ret = -ENOMEM; 9888c2ecf20Sopenharmony_ci goto failed_ioremap; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci fbi->map_size = PAGE_ALIGN(info->fix.smem_len); 9928c2ecf20Sopenharmony_ci info->screen_buffer = dma_alloc_wc(&pdev->dev, fbi->map_size, 9938c2ecf20Sopenharmony_ci &fbi->map_dma, GFP_KERNEL); 9948c2ecf20Sopenharmony_ci if (!info->screen_buffer) { 9958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate video RAM\n"); 9968c2ecf20Sopenharmony_ci ret = -ENOMEM; 9978c2ecf20Sopenharmony_ci goto failed_map; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci info->fix.smem_start = fbi->map_dma; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (pdata && pdata->init) { 10038c2ecf20Sopenharmony_ci ret = pdata->init(fbi->pdev); 10048c2ecf20Sopenharmony_ci if (ret) 10058c2ecf20Sopenharmony_ci goto failed_platform_init; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 10108c2ecf20Sopenharmony_ci for (i = 0; i < fbi->num_modes; i++) 10118c2ecf20Sopenharmony_ci fb_add_videomode(&fbi->mode[i].mode, &info->modelist); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* 10148c2ecf20Sopenharmony_ci * This makes sure that our colour bitfield 10158c2ecf20Sopenharmony_ci * descriptors are correctly initialised. 10168c2ecf20Sopenharmony_ci */ 10178c2ecf20Sopenharmony_ci imxfb_check_var(&info->var, info); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* 10208c2ecf20Sopenharmony_ci * For modes > 8bpp, the color map is bypassed. 10218c2ecf20Sopenharmony_ci * Therefore, 256 entries are enough. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 0); 10248c2ecf20Sopenharmony_ci if (ret < 0) 10258c2ecf20Sopenharmony_ci goto failed_cmap; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci imxfb_set_par(info); 10288c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 10298c2ecf20Sopenharmony_ci if (ret < 0) { 10308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register framebuffer\n"); 10318c2ecf20Sopenharmony_ci goto failed_register; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci fbi->lcd_pwr = devm_regulator_get(&pdev->dev, "lcd"); 10358c2ecf20Sopenharmony_ci if (PTR_ERR(fbi->lcd_pwr) == -EPROBE_DEFER) { 10368c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 10378c2ecf20Sopenharmony_ci goto failed_lcd; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci lcd = devm_lcd_device_register(&pdev->dev, "imxfb-lcd", &pdev->dev, fbi, 10418c2ecf20Sopenharmony_ci &imxfb_lcd_ops); 10428c2ecf20Sopenharmony_ci if (IS_ERR(lcd)) { 10438c2ecf20Sopenharmony_ci ret = PTR_ERR(lcd); 10448c2ecf20Sopenharmony_ci goto failed_lcd; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci lcd->props.max_contrast = 0xff; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci imxfb_enable_controller(fbi); 10508c2ecf20Sopenharmony_ci fbi->pdev = pdev; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci return 0; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cifailed_lcd: 10558c2ecf20Sopenharmony_ci unregister_framebuffer(info); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cifailed_register: 10588c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 10598c2ecf20Sopenharmony_cifailed_cmap: 10608c2ecf20Sopenharmony_ci if (pdata && pdata->exit) 10618c2ecf20Sopenharmony_ci pdata->exit(fbi->pdev); 10628c2ecf20Sopenharmony_cifailed_platform_init: 10638c2ecf20Sopenharmony_ci dma_free_wc(&pdev->dev, fbi->map_size, info->screen_buffer, 10648c2ecf20Sopenharmony_ci fbi->map_dma); 10658c2ecf20Sopenharmony_cifailed_map: 10668c2ecf20Sopenharmony_ci iounmap(fbi->regs); 10678c2ecf20Sopenharmony_cifailed_ioremap: 10688c2ecf20Sopenharmony_cifailed_getclock: 10698c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 10708c2ecf20Sopenharmony_cifailed_req: 10718c2ecf20Sopenharmony_cifailed_of_parse: 10728c2ecf20Sopenharmony_ci kfree(info->pseudo_palette); 10738c2ecf20Sopenharmony_cifailed_init: 10748c2ecf20Sopenharmony_ci framebuffer_release(info); 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic int imxfb_remove(struct platform_device *pdev) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct imx_fb_platform_data *pdata; 10818c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 10828c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 10838c2ecf20Sopenharmony_ci struct resource *res; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci imxfb_disable_controller(fbi); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci unregister_framebuffer(info); 10908c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 10918c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 10928c2ecf20Sopenharmony_ci if (pdata && pdata->exit) 10938c2ecf20Sopenharmony_ci pdata->exit(fbi->pdev); 10948c2ecf20Sopenharmony_ci dma_free_wc(&pdev->dev, fbi->map_size, info->screen_buffer, 10958c2ecf20Sopenharmony_ci fbi->map_dma); 10968c2ecf20Sopenharmony_ci iounmap(fbi->regs); 10978c2ecf20Sopenharmony_ci release_mem_region(res->start, resource_size(res)); 10988c2ecf20Sopenharmony_ci kfree(info->pseudo_palette); 10998c2ecf20Sopenharmony_ci framebuffer_release(info); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci return 0; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int __maybe_unused imxfb_suspend(struct device *dev) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11078c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci imxfb_disable_controller(fbi); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci return 0; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic int __maybe_unused imxfb_resume(struct device *dev) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11178c2ecf20Sopenharmony_ci struct imxfb_info *fbi = info->par; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci imxfb_enable_controller(fbi); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci return 0; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(imxfb_pm_ops, imxfb_suspend, imxfb_resume); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic struct platform_driver imxfb_driver = { 11278c2ecf20Sopenharmony_ci .driver = { 11288c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 11298c2ecf20Sopenharmony_ci .of_match_table = imxfb_of_dev_id, 11308c2ecf20Sopenharmony_ci .pm = &imxfb_pm_ops, 11318c2ecf20Sopenharmony_ci }, 11328c2ecf20Sopenharmony_ci .probe = imxfb_probe, 11338c2ecf20Sopenharmony_ci .remove = imxfb_remove, 11348c2ecf20Sopenharmony_ci .id_table = imxfb_devtype, 11358c2ecf20Sopenharmony_ci}; 11368c2ecf20Sopenharmony_cimodule_platform_driver(imxfb_driver); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale i.MX framebuffer driver"); 11398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, Pengutronix"); 11408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1141