18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008 48c2ecf20Sopenharmony_ci * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/fb.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/ioport.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 238c2ecf20Sopenharmony_ci#include <linux/console.h> 248c2ecf20Sopenharmony_ci#include <linux/clk.h> 258c2ecf20Sopenharmony_ci#include <linux/mutex.h> 268c2ecf20Sopenharmony_ci#include <linux/dma/ipu-dma.h> 278c2ecf20Sopenharmony_ci#include <linux/backlight.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/platform_data/dma-imx.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_data/video-mx3fb.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <asm/io.h> 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MX3FB_NAME "mx3_sdc_fb" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MX3FB_REG_OFFSET 0xB4 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* SDC Registers */ 408c2ecf20Sopenharmony_ci#define SDC_COM_CONF (0xB4 - MX3FB_REG_OFFSET) 418c2ecf20Sopenharmony_ci#define SDC_GW_CTRL (0xB8 - MX3FB_REG_OFFSET) 428c2ecf20Sopenharmony_ci#define SDC_FG_POS (0xBC - MX3FB_REG_OFFSET) 438c2ecf20Sopenharmony_ci#define SDC_BG_POS (0xC0 - MX3FB_REG_OFFSET) 448c2ecf20Sopenharmony_ci#define SDC_CUR_POS (0xC4 - MX3FB_REG_OFFSET) 458c2ecf20Sopenharmony_ci#define SDC_PWM_CTRL (0xC8 - MX3FB_REG_OFFSET) 468c2ecf20Sopenharmony_ci#define SDC_CUR_MAP (0xCC - MX3FB_REG_OFFSET) 478c2ecf20Sopenharmony_ci#define SDC_HOR_CONF (0xD0 - MX3FB_REG_OFFSET) 488c2ecf20Sopenharmony_ci#define SDC_VER_CONF (0xD4 - MX3FB_REG_OFFSET) 498c2ecf20Sopenharmony_ci#define SDC_SHARP_CONF_1 (0xD8 - MX3FB_REG_OFFSET) 508c2ecf20Sopenharmony_ci#define SDC_SHARP_CONF_2 (0xDC - MX3FB_REG_OFFSET) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Register bits */ 538c2ecf20Sopenharmony_ci#define SDC_COM_TFT_COLOR 0x00000001UL 548c2ecf20Sopenharmony_ci#define SDC_COM_FG_EN 0x00000010UL 558c2ecf20Sopenharmony_ci#define SDC_COM_GWSEL 0x00000020UL 568c2ecf20Sopenharmony_ci#define SDC_COM_GLB_A 0x00000040UL 578c2ecf20Sopenharmony_ci#define SDC_COM_KEY_COLOR_G 0x00000080UL 588c2ecf20Sopenharmony_ci#define SDC_COM_BG_EN 0x00000200UL 598c2ecf20Sopenharmony_ci#define SDC_COM_SHARP 0x00001000UL 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define SDC_V_SYNC_WIDTH_L 0x00000001UL 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Display Interface registers */ 648c2ecf20Sopenharmony_ci#define DI_DISP_IF_CONF (0x0124 - MX3FB_REG_OFFSET) 658c2ecf20Sopenharmony_ci#define DI_DISP_SIG_POL (0x0128 - MX3FB_REG_OFFSET) 668c2ecf20Sopenharmony_ci#define DI_SER_DISP1_CONF (0x012C - MX3FB_REG_OFFSET) 678c2ecf20Sopenharmony_ci#define DI_SER_DISP2_CONF (0x0130 - MX3FB_REG_OFFSET) 688c2ecf20Sopenharmony_ci#define DI_HSP_CLK_PER (0x0134 - MX3FB_REG_OFFSET) 698c2ecf20Sopenharmony_ci#define DI_DISP0_TIME_CONF_1 (0x0138 - MX3FB_REG_OFFSET) 708c2ecf20Sopenharmony_ci#define DI_DISP0_TIME_CONF_2 (0x013C - MX3FB_REG_OFFSET) 718c2ecf20Sopenharmony_ci#define DI_DISP0_TIME_CONF_3 (0x0140 - MX3FB_REG_OFFSET) 728c2ecf20Sopenharmony_ci#define DI_DISP1_TIME_CONF_1 (0x0144 - MX3FB_REG_OFFSET) 738c2ecf20Sopenharmony_ci#define DI_DISP1_TIME_CONF_2 (0x0148 - MX3FB_REG_OFFSET) 748c2ecf20Sopenharmony_ci#define DI_DISP1_TIME_CONF_3 (0x014C - MX3FB_REG_OFFSET) 758c2ecf20Sopenharmony_ci#define DI_DISP2_TIME_CONF_1 (0x0150 - MX3FB_REG_OFFSET) 768c2ecf20Sopenharmony_ci#define DI_DISP2_TIME_CONF_2 (0x0154 - MX3FB_REG_OFFSET) 778c2ecf20Sopenharmony_ci#define DI_DISP2_TIME_CONF_3 (0x0158 - MX3FB_REG_OFFSET) 788c2ecf20Sopenharmony_ci#define DI_DISP3_TIME_CONF (0x015C - MX3FB_REG_OFFSET) 798c2ecf20Sopenharmony_ci#define DI_DISP0_DB0_MAP (0x0160 - MX3FB_REG_OFFSET) 808c2ecf20Sopenharmony_ci#define DI_DISP0_DB1_MAP (0x0164 - MX3FB_REG_OFFSET) 818c2ecf20Sopenharmony_ci#define DI_DISP0_DB2_MAP (0x0168 - MX3FB_REG_OFFSET) 828c2ecf20Sopenharmony_ci#define DI_DISP0_CB0_MAP (0x016C - MX3FB_REG_OFFSET) 838c2ecf20Sopenharmony_ci#define DI_DISP0_CB1_MAP (0x0170 - MX3FB_REG_OFFSET) 848c2ecf20Sopenharmony_ci#define DI_DISP0_CB2_MAP (0x0174 - MX3FB_REG_OFFSET) 858c2ecf20Sopenharmony_ci#define DI_DISP1_DB0_MAP (0x0178 - MX3FB_REG_OFFSET) 868c2ecf20Sopenharmony_ci#define DI_DISP1_DB1_MAP (0x017C - MX3FB_REG_OFFSET) 878c2ecf20Sopenharmony_ci#define DI_DISP1_DB2_MAP (0x0180 - MX3FB_REG_OFFSET) 888c2ecf20Sopenharmony_ci#define DI_DISP1_CB0_MAP (0x0184 - MX3FB_REG_OFFSET) 898c2ecf20Sopenharmony_ci#define DI_DISP1_CB1_MAP (0x0188 - MX3FB_REG_OFFSET) 908c2ecf20Sopenharmony_ci#define DI_DISP1_CB2_MAP (0x018C - MX3FB_REG_OFFSET) 918c2ecf20Sopenharmony_ci#define DI_DISP2_DB0_MAP (0x0190 - MX3FB_REG_OFFSET) 928c2ecf20Sopenharmony_ci#define DI_DISP2_DB1_MAP (0x0194 - MX3FB_REG_OFFSET) 938c2ecf20Sopenharmony_ci#define DI_DISP2_DB2_MAP (0x0198 - MX3FB_REG_OFFSET) 948c2ecf20Sopenharmony_ci#define DI_DISP2_CB0_MAP (0x019C - MX3FB_REG_OFFSET) 958c2ecf20Sopenharmony_ci#define DI_DISP2_CB1_MAP (0x01A0 - MX3FB_REG_OFFSET) 968c2ecf20Sopenharmony_ci#define DI_DISP2_CB2_MAP (0x01A4 - MX3FB_REG_OFFSET) 978c2ecf20Sopenharmony_ci#define DI_DISP3_B0_MAP (0x01A8 - MX3FB_REG_OFFSET) 988c2ecf20Sopenharmony_ci#define DI_DISP3_B1_MAP (0x01AC - MX3FB_REG_OFFSET) 998c2ecf20Sopenharmony_ci#define DI_DISP3_B2_MAP (0x01B0 - MX3FB_REG_OFFSET) 1008c2ecf20Sopenharmony_ci#define DI_DISP_ACC_CC (0x01B4 - MX3FB_REG_OFFSET) 1018c2ecf20Sopenharmony_ci#define DI_DISP_LLA_CONF (0x01B8 - MX3FB_REG_OFFSET) 1028c2ecf20Sopenharmony_ci#define DI_DISP_LLA_DATA (0x01BC - MX3FB_REG_OFFSET) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* DI_DISP_SIG_POL bits */ 1058c2ecf20Sopenharmony_ci#define DI_D3_VSYNC_POL_SHIFT 28 1068c2ecf20Sopenharmony_ci#define DI_D3_HSYNC_POL_SHIFT 27 1078c2ecf20Sopenharmony_ci#define DI_D3_DRDY_SHARP_POL_SHIFT 26 1088c2ecf20Sopenharmony_ci#define DI_D3_CLK_POL_SHIFT 25 1098c2ecf20Sopenharmony_ci#define DI_D3_DATA_POL_SHIFT 24 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* DI_DISP_IF_CONF bits */ 1128c2ecf20Sopenharmony_ci#define DI_D3_CLK_IDLE_SHIFT 26 1138c2ecf20Sopenharmony_ci#define DI_D3_CLK_SEL_SHIFT 25 1148c2ecf20Sopenharmony_ci#define DI_D3_DATAMSK_SHIFT 24 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cienum ipu_panel { 1178c2ecf20Sopenharmony_ci IPU_PANEL_SHARP_TFT, 1188c2ecf20Sopenharmony_ci IPU_PANEL_TFT, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistruct ipu_di_signal_cfg { 1228c2ecf20Sopenharmony_ci unsigned datamask_en:1; 1238c2ecf20Sopenharmony_ci unsigned clksel_en:1; 1248c2ecf20Sopenharmony_ci unsigned clkidle_en:1; 1258c2ecf20Sopenharmony_ci unsigned data_pol:1; /* true = inverted */ 1268c2ecf20Sopenharmony_ci unsigned clk_pol:1; /* true = rising edge */ 1278c2ecf20Sopenharmony_ci unsigned enable_pol:1; 1288c2ecf20Sopenharmony_ci unsigned Hsync_pol:1; /* true = active high */ 1298c2ecf20Sopenharmony_ci unsigned Vsync_pol:1; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct fb_videomode mx3fb_modedb[] = { 1338c2ecf20Sopenharmony_ci { 1348c2ecf20Sopenharmony_ci /* 240x320 @ 60 Hz */ 1358c2ecf20Sopenharmony_ci .name = "Sharp-QVGA", 1368c2ecf20Sopenharmony_ci .refresh = 60, 1378c2ecf20Sopenharmony_ci .xres = 240, 1388c2ecf20Sopenharmony_ci .yres = 320, 1398c2ecf20Sopenharmony_ci .pixclock = 185925, 1408c2ecf20Sopenharmony_ci .left_margin = 9, 1418c2ecf20Sopenharmony_ci .right_margin = 16, 1428c2ecf20Sopenharmony_ci .upper_margin = 7, 1438c2ecf20Sopenharmony_ci .lower_margin = 9, 1448c2ecf20Sopenharmony_ci .hsync_len = 1, 1458c2ecf20Sopenharmony_ci .vsync_len = 1, 1468c2ecf20Sopenharmony_ci .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | 1478c2ecf20Sopenharmony_ci FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | 1488c2ecf20Sopenharmony_ci FB_SYNC_CLK_IDLE_EN, 1498c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1508c2ecf20Sopenharmony_ci .flag = 0, 1518c2ecf20Sopenharmony_ci }, { 1528c2ecf20Sopenharmony_ci /* 240x33 @ 60 Hz */ 1538c2ecf20Sopenharmony_ci .name = "Sharp-CLI", 1548c2ecf20Sopenharmony_ci .refresh = 60, 1558c2ecf20Sopenharmony_ci .xres = 240, 1568c2ecf20Sopenharmony_ci .yres = 33, 1578c2ecf20Sopenharmony_ci .pixclock = 185925, 1588c2ecf20Sopenharmony_ci .left_margin = 9, 1598c2ecf20Sopenharmony_ci .right_margin = 16, 1608c2ecf20Sopenharmony_ci .upper_margin = 7, 1618c2ecf20Sopenharmony_ci .lower_margin = 9 + 287, 1628c2ecf20Sopenharmony_ci .hsync_len = 1, 1638c2ecf20Sopenharmony_ci .vsync_len = 1, 1648c2ecf20Sopenharmony_ci .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | 1658c2ecf20Sopenharmony_ci FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | 1668c2ecf20Sopenharmony_ci FB_SYNC_CLK_IDLE_EN, 1678c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1688c2ecf20Sopenharmony_ci .flag = 0, 1698c2ecf20Sopenharmony_ci }, { 1708c2ecf20Sopenharmony_ci /* 640x480 @ 60 Hz */ 1718c2ecf20Sopenharmony_ci .name = "NEC-VGA", 1728c2ecf20Sopenharmony_ci .refresh = 60, 1738c2ecf20Sopenharmony_ci .xres = 640, 1748c2ecf20Sopenharmony_ci .yres = 480, 1758c2ecf20Sopenharmony_ci .pixclock = 38255, 1768c2ecf20Sopenharmony_ci .left_margin = 144, 1778c2ecf20Sopenharmony_ci .right_margin = 0, 1788c2ecf20Sopenharmony_ci .upper_margin = 34, 1798c2ecf20Sopenharmony_ci .lower_margin = 40, 1808c2ecf20Sopenharmony_ci .hsync_len = 1, 1818c2ecf20Sopenharmony_ci .vsync_len = 1, 1828c2ecf20Sopenharmony_ci .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, 1838c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1848c2ecf20Sopenharmony_ci .flag = 0, 1858c2ecf20Sopenharmony_ci }, { 1868c2ecf20Sopenharmony_ci /* NTSC TV output */ 1878c2ecf20Sopenharmony_ci .name = "TV-NTSC", 1888c2ecf20Sopenharmony_ci .refresh = 60, 1898c2ecf20Sopenharmony_ci .xres = 640, 1908c2ecf20Sopenharmony_ci .yres = 480, 1918c2ecf20Sopenharmony_ci .pixclock = 37538, 1928c2ecf20Sopenharmony_ci .left_margin = 38, 1938c2ecf20Sopenharmony_ci .right_margin = 858 - 640 - 38 - 3, 1948c2ecf20Sopenharmony_ci .upper_margin = 36, 1958c2ecf20Sopenharmony_ci .lower_margin = 518 - 480 - 36 - 1, 1968c2ecf20Sopenharmony_ci .hsync_len = 3, 1978c2ecf20Sopenharmony_ci .vsync_len = 1, 1988c2ecf20Sopenharmony_ci .sync = 0, 1998c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 2008c2ecf20Sopenharmony_ci .flag = 0, 2018c2ecf20Sopenharmony_ci }, { 2028c2ecf20Sopenharmony_ci /* PAL TV output */ 2038c2ecf20Sopenharmony_ci .name = "TV-PAL", 2048c2ecf20Sopenharmony_ci .refresh = 50, 2058c2ecf20Sopenharmony_ci .xres = 640, 2068c2ecf20Sopenharmony_ci .yres = 480, 2078c2ecf20Sopenharmony_ci .pixclock = 37538, 2088c2ecf20Sopenharmony_ci .left_margin = 38, 2098c2ecf20Sopenharmony_ci .right_margin = 960 - 640 - 38 - 32, 2108c2ecf20Sopenharmony_ci .upper_margin = 32, 2118c2ecf20Sopenharmony_ci .lower_margin = 555 - 480 - 32 - 3, 2128c2ecf20Sopenharmony_ci .hsync_len = 32, 2138c2ecf20Sopenharmony_ci .vsync_len = 3, 2148c2ecf20Sopenharmony_ci .sync = 0, 2158c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 2168c2ecf20Sopenharmony_ci .flag = 0, 2178c2ecf20Sopenharmony_ci }, { 2188c2ecf20Sopenharmony_ci /* TV output VGA mode, 640x480 @ 65 Hz */ 2198c2ecf20Sopenharmony_ci .name = "TV-VGA", 2208c2ecf20Sopenharmony_ci .refresh = 60, 2218c2ecf20Sopenharmony_ci .xres = 640, 2228c2ecf20Sopenharmony_ci .yres = 480, 2238c2ecf20Sopenharmony_ci .pixclock = 40574, 2248c2ecf20Sopenharmony_ci .left_margin = 35, 2258c2ecf20Sopenharmony_ci .right_margin = 45, 2268c2ecf20Sopenharmony_ci .upper_margin = 9, 2278c2ecf20Sopenharmony_ci .lower_margin = 1, 2288c2ecf20Sopenharmony_ci .hsync_len = 46, 2298c2ecf20Sopenharmony_ci .vsync_len = 5, 2308c2ecf20Sopenharmony_ci .sync = 0, 2318c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 2328c2ecf20Sopenharmony_ci .flag = 0, 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistruct mx3fb_data { 2378c2ecf20Sopenharmony_ci struct fb_info *fbi; 2388c2ecf20Sopenharmony_ci int backlight_level; 2398c2ecf20Sopenharmony_ci void __iomem *reg_base; 2408c2ecf20Sopenharmony_ci spinlock_t lock; 2418c2ecf20Sopenharmony_ci struct device *dev; 2428c2ecf20Sopenharmony_ci struct backlight_device *bl; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci uint32_t h_start_width; 2458c2ecf20Sopenharmony_ci uint32_t v_start_width; 2468c2ecf20Sopenharmony_ci enum disp_data_mapping disp_data_fmt; 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistruct dma_chan_request { 2508c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb; 2518c2ecf20Sopenharmony_ci enum ipu_channel id; 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* MX3 specific framebuffer information. */ 2558c2ecf20Sopenharmony_cistruct mx3fb_info { 2568c2ecf20Sopenharmony_ci int blank; 2578c2ecf20Sopenharmony_ci enum ipu_channel ipu_ch; 2588c2ecf20Sopenharmony_ci uint32_t cur_ipu_buf; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci struct completion flip_cmpl; 2638c2ecf20Sopenharmony_ci struct mutex mutex; /* Protects fb-ops */ 2648c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb; 2658c2ecf20Sopenharmony_ci struct idmac_channel *idmac_channel; 2668c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd; 2678c2ecf20Sopenharmony_ci dma_cookie_t cookie; 2688c2ecf20Sopenharmony_ci struct scatterlist sg[2]; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci struct fb_var_screeninfo cur_var; /* current var info */ 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value); 2748c2ecf20Sopenharmony_cistatic u32 sdc_get_brightness(struct mx3fb_data *mx3fb); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int mx3fb_bl_get_brightness(struct backlight_device *bl) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct mx3fb_data *fbd = bl_get_data(bl); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return sdc_get_brightness(fbd); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int mx3fb_bl_update_status(struct backlight_device *bl) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct mx3fb_data *fbd = bl_get_data(bl); 2868c2ecf20Sopenharmony_ci int brightness = bl->props.brightness; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (bl->props.power != FB_BLANK_UNBLANK) 2898c2ecf20Sopenharmony_ci brightness = 0; 2908c2ecf20Sopenharmony_ci if (bl->props.fb_blank != FB_BLANK_UNBLANK) 2918c2ecf20Sopenharmony_ci brightness = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci fbd->backlight_level = (fbd->backlight_level & ~0xFF) | brightness; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci sdc_set_brightness(fbd, fbd->backlight_level); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct backlight_ops mx3fb_lcdc_bl_ops = { 3018c2ecf20Sopenharmony_ci .update_status = mx3fb_bl_update_status, 3028c2ecf20Sopenharmony_ci .get_brightness = mx3fb_bl_get_brightness, 3038c2ecf20Sopenharmony_ci}; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic void mx3fb_init_backlight(struct mx3fb_data *fbd) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct backlight_properties props; 3088c2ecf20Sopenharmony_ci struct backlight_device *bl; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (fbd->bl) 3118c2ecf20Sopenharmony_ci return; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 3148c2ecf20Sopenharmony_ci props.max_brightness = 0xff; 3158c2ecf20Sopenharmony_ci props.type = BACKLIGHT_RAW; 3168c2ecf20Sopenharmony_ci sdc_set_brightness(fbd, fbd->backlight_level); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci bl = backlight_device_register("mx3fb-bl", fbd->dev, fbd, 3198c2ecf20Sopenharmony_ci &mx3fb_lcdc_bl_ops, &props); 3208c2ecf20Sopenharmony_ci if (IS_ERR(bl)) { 3218c2ecf20Sopenharmony_ci dev_err(fbd->dev, "error %ld on backlight register\n", 3228c2ecf20Sopenharmony_ci PTR_ERR(bl)); 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci fbd->bl = bl; 3278c2ecf20Sopenharmony_ci bl->props.power = FB_BLANK_UNBLANK; 3288c2ecf20Sopenharmony_ci bl->props.fb_blank = FB_BLANK_UNBLANK; 3298c2ecf20Sopenharmony_ci bl->props.brightness = mx3fb_bl_get_brightness(bl); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void mx3fb_exit_backlight(struct mx3fb_data *fbd) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci backlight_device_unregister(fbd->bl); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void mx3fb_dma_done(void *); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */ 3408c2ecf20Sopenharmony_cistatic const char *fb_mode; 3418c2ecf20Sopenharmony_cistatic unsigned long default_bpp = 16; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic u32 mx3fb_read_reg(struct mx3fb_data *mx3fb, unsigned long reg) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci return __raw_readl(mx3fb->reg_base + reg); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void mx3fb_write_reg(struct mx3fb_data *mx3fb, u32 value, unsigned long reg) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci __raw_writel(value, mx3fb->reg_base + reg); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistruct di_mapping { 3548c2ecf20Sopenharmony_ci uint32_t b0, b1, b2; 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic const struct di_mapping di_mappings[] = { 3588c2ecf20Sopenharmony_ci [IPU_DISP_DATA_MAPPING_RGB666] = { 0x0005000f, 0x000b000f, 0x0011000f }, 3598c2ecf20Sopenharmony_ci [IPU_DISP_DATA_MAPPING_RGB565] = { 0x0004003f, 0x000a000f, 0x000f003f }, 3608c2ecf20Sopenharmony_ci [IPU_DISP_DATA_MAPPING_RGB888] = { 0x00070000, 0x000f0000, 0x00170000 }, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void sdc_fb_init(struct mx3fb_info *fbi) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = fbi->mx3fb; 3668c2ecf20Sopenharmony_ci uint32_t reg; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* Returns enabled flag before uninit */ 3748c2ecf20Sopenharmony_cistatic uint32_t sdc_fb_uninit(struct mx3fb_info *fbi) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = fbi->mx3fb; 3778c2ecf20Sopenharmony_ci uint32_t reg; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return reg & SDC_COM_BG_EN; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void sdc_enable_channel(struct mx3fb_info *mx3_fbi) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; 3898c2ecf20Sopenharmony_ci struct idmac_channel *ichan = mx3_fbi->idmac_channel; 3908c2ecf20Sopenharmony_ci struct dma_chan *dma_chan = &ichan->dma_chan; 3918c2ecf20Sopenharmony_ci unsigned long flags; 3928c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (mx3_fbi->txd) 3958c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "mx3fbi %p, desc %p, sg %p\n", mx3_fbi, 3968c2ecf20Sopenharmony_ci to_tx_desc(mx3_fbi->txd), to_tx_desc(mx3_fbi->txd)->sg); 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "mx3fbi %p, txd = NULL\n", mx3_fbi); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* This enables the channel */ 4018c2ecf20Sopenharmony_ci if (mx3_fbi->cookie < 0) { 4028c2ecf20Sopenharmony_ci mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan, 4038c2ecf20Sopenharmony_ci &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); 4048c2ecf20Sopenharmony_ci if (!mx3_fbi->txd) { 4058c2ecf20Sopenharmony_ci dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", 4068c2ecf20Sopenharmony_ci dma_chan->chan_id); 4078c2ecf20Sopenharmony_ci return; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci mx3_fbi->txd->callback_param = mx3_fbi->txd; 4118c2ecf20Sopenharmony_ci mx3_fbi->txd->callback = mx3fb_dma_done; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci cookie = mx3_fbi->txd->tx_submit(mx3_fbi->txd); 4148c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "%d: Submit %p #%d [%c]\n", __LINE__, 4158c2ecf20Sopenharmony_ci mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); 4168c2ecf20Sopenharmony_ci } else { 4178c2ecf20Sopenharmony_ci if (!mx3_fbi->txd || !mx3_fbi->txd->tx_submit) { 4188c2ecf20Sopenharmony_ci dev_err(mx3fb->dev, "Cannot enable channel %d\n", 4198c2ecf20Sopenharmony_ci dma_chan->chan_id); 4208c2ecf20Sopenharmony_ci return; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Just re-activate the same buffer */ 4248c2ecf20Sopenharmony_ci dma_async_issue_pending(dma_chan); 4258c2ecf20Sopenharmony_ci cookie = mx3_fbi->cookie; 4268c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "%d: Re-submit %p #%d [%c]\n", __LINE__, 4278c2ecf20Sopenharmony_ci mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (cookie >= 0) { 4318c2ecf20Sopenharmony_ci spin_lock_irqsave(&mx3fb->lock, flags); 4328c2ecf20Sopenharmony_ci sdc_fb_init(mx3_fbi); 4338c2ecf20Sopenharmony_ci mx3_fbi->cookie = cookie; 4348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mx3fb->lock, flags); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* 4388c2ecf20Sopenharmony_ci * Attention! Without this msleep the channel keeps generating 4398c2ecf20Sopenharmony_ci * interrupts. Next sdc_set_brightness() is going to be called 4408c2ecf20Sopenharmony_ci * from mx3fb_blank(). 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci msleep(2); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void sdc_disable_channel(struct mx3fb_info *mx3_fbi) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; 4488c2ecf20Sopenharmony_ci uint32_t enabled; 4498c2ecf20Sopenharmony_ci unsigned long flags; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (mx3_fbi->txd == NULL) 4528c2ecf20Sopenharmony_ci return; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_lock_irqsave(&mx3fb->lock, flags); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci enabled = sdc_fb_uninit(mx3_fbi); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mx3fb->lock, flags); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dmaengine_terminate_all(mx3_fbi->txd->chan); 4618c2ecf20Sopenharmony_ci mx3_fbi->txd = NULL; 4628c2ecf20Sopenharmony_ci mx3_fbi->cookie = -EINVAL; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/** 4668c2ecf20Sopenharmony_ci * sdc_set_window_pos() - set window position of the respective plane. 4678c2ecf20Sopenharmony_ci * @mx3fb: mx3fb context. 4688c2ecf20Sopenharmony_ci * @channel: IPU DMAC channel ID. 4698c2ecf20Sopenharmony_ci * @x_pos: X coordinate relative to the top left corner to place window at. 4708c2ecf20Sopenharmony_ci * @y_pos: Y coordinate relative to the top left corner to place window at. 4718c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel, 4748c2ecf20Sopenharmony_ci int16_t x_pos, int16_t y_pos) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci if (channel != IDMAC_SDC_0) 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci x_pos += mx3fb->h_start_width; 4808c2ecf20Sopenharmony_ci y_pos += mx3fb->v_start_width; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/** 4878c2ecf20Sopenharmony_ci * sdc_init_panel() - initialize a synchronous LCD panel. 4888c2ecf20Sopenharmony_ci * @mx3fb: mx3fb context. 4898c2ecf20Sopenharmony_ci * @panel: panel type. 4908c2ecf20Sopenharmony_ci * @pixel_clk: desired pixel clock frequency in Hz. 4918c2ecf20Sopenharmony_ci * @width: width of panel in pixels. 4928c2ecf20Sopenharmony_ci * @height: height of panel in pixels. 4938c2ecf20Sopenharmony_ci * @h_start_width: number of pixel clocks between the HSYNC signal pulse 4948c2ecf20Sopenharmony_ci * and the start of valid data. 4958c2ecf20Sopenharmony_ci * @h_sync_width: width of the HSYNC signal in units of pixel clocks. 4968c2ecf20Sopenharmony_ci * @h_end_width: number of pixel clocks between the end of valid data 4978c2ecf20Sopenharmony_ci * and the HSYNC signal for next line. 4988c2ecf20Sopenharmony_ci * @v_start_width: number of lines between the VSYNC signal pulse and the 4998c2ecf20Sopenharmony_ci * start of valid data. 5008c2ecf20Sopenharmony_ci * @v_sync_width: width of the VSYNC signal in units of lines 5018c2ecf20Sopenharmony_ci * @v_end_width: number of lines between the end of valid data and the 5028c2ecf20Sopenharmony_ci * VSYNC signal for next frame. 5038c2ecf20Sopenharmony_ci * @sig: bitfield of signal polarities for LCD interface. 5048c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, 5078c2ecf20Sopenharmony_ci uint32_t pixel_clk, 5088c2ecf20Sopenharmony_ci uint16_t width, uint16_t height, 5098c2ecf20Sopenharmony_ci uint16_t h_start_width, uint16_t h_sync_width, 5108c2ecf20Sopenharmony_ci uint16_t h_end_width, uint16_t v_start_width, 5118c2ecf20Sopenharmony_ci uint16_t v_sync_width, uint16_t v_end_width, 5128c2ecf20Sopenharmony_ci const struct ipu_di_signal_cfg *sig) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci unsigned long lock_flags; 5158c2ecf20Sopenharmony_ci uint32_t reg; 5168c2ecf20Sopenharmony_ci uint32_t old_conf; 5178c2ecf20Sopenharmony_ci uint32_t div; 5188c2ecf20Sopenharmony_ci struct clk *ipu_clk; 5198c2ecf20Sopenharmony_ci const struct di_mapping *map; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (v_sync_width == 0 || h_sync_width == 0) 5248c2ecf20Sopenharmony_ci return -EINVAL; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Init panel size and blanking periods */ 5278c2ecf20Sopenharmony_ci reg = ((uint32_t) (h_sync_width - 1) << 26) | 5288c2ecf20Sopenharmony_ci ((uint32_t) (width + h_start_width + h_end_width - 1) << 16); 5298c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg, SDC_HOR_CONF); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci#ifdef DEBUG 5328c2ecf20Sopenharmony_ci printk(KERN_CONT " hor_conf %x,", reg); 5338c2ecf20Sopenharmony_ci#endif 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L | 5368c2ecf20Sopenharmony_ci ((uint32_t) (height + v_start_width + v_end_width - 1) << 16); 5378c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg, SDC_VER_CONF); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci#ifdef DEBUG 5408c2ecf20Sopenharmony_ci printk(KERN_CONT " ver_conf %x\n", reg); 5418c2ecf20Sopenharmony_ci#endif 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci mx3fb->h_start_width = h_start_width; 5448c2ecf20Sopenharmony_ci mx3fb->v_start_width = v_start_width; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci switch (panel) { 5478c2ecf20Sopenharmony_ci case IPU_PANEL_SHARP_TFT: 5488c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1); 5498c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2); 5508c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci case IPU_PANEL_TFT: 5538c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF); 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci default: 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Init clocking */ 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * Calculate divider: fractional part is 4 bits so simply multiple by 5638c2ecf20Sopenharmony_ci * 2^4 to get fractional part, as long as we stay under ~250MHz and on 5648c2ecf20Sopenharmony_ci * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci ipu_clk = clk_get(mx3fb->dev, NULL); 5678c2ecf20Sopenharmony_ci if (!IS_ERR(ipu_clk)) { 5688c2ecf20Sopenharmony_ci div = clk_get_rate(ipu_clk) * 16 / pixel_clk; 5698c2ecf20Sopenharmony_ci clk_put(ipu_clk); 5708c2ecf20Sopenharmony_ci } else { 5718c2ecf20Sopenharmony_ci div = 0; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (div < 0x40) { /* Divider less than 4 */ 5758c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, 5768c2ecf20Sopenharmony_ci "InitPanel() - Pixel clock divider less than 4\n"); 5778c2ecf20Sopenharmony_ci div = 0x40; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "pixel clk = %u, divider %u.%u\n", 5818c2ecf20Sopenharmony_ci pixel_clk, div >> 4, (div & 7) * 125); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci spin_lock_irqsave(&mx3fb->lock, lock_flags); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* 5868c2ecf20Sopenharmony_ci * DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits 5878c2ecf20Sopenharmony_ci * fewer. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing 5888c2ecf20Sopenharmony_ci * debug. DISP3_IF_CLK_UP_WR is 0 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, (((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* DI settings */ 5938c2ecf20Sopenharmony_ci old_conf = mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF) & 0x78FFFFFF; 5948c2ecf20Sopenharmony_ci old_conf |= sig->datamask_en << DI_D3_DATAMSK_SHIFT | 5958c2ecf20Sopenharmony_ci sig->clksel_en << DI_D3_CLK_SEL_SHIFT | 5968c2ecf20Sopenharmony_ci sig->clkidle_en << DI_D3_CLK_IDLE_SHIFT; 5978c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, old_conf, DI_DISP_IF_CONF); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci old_conf = mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL) & 0xE0FFFFFF; 6008c2ecf20Sopenharmony_ci old_conf |= sig->data_pol << DI_D3_DATA_POL_SHIFT | 6018c2ecf20Sopenharmony_ci sig->clk_pol << DI_D3_CLK_POL_SHIFT | 6028c2ecf20Sopenharmony_ci sig->enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT | 6038c2ecf20Sopenharmony_ci sig->Hsync_pol << DI_D3_HSYNC_POL_SHIFT | 6048c2ecf20Sopenharmony_ci sig->Vsync_pol << DI_D3_VSYNC_POL_SHIFT; 6058c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci map = &di_mappings[mx3fb->disp_data_fmt]; 6088c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, map->b0, DI_DISP3_B0_MAP); 6098c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, map->b1, DI_DISP3_B1_MAP); 6108c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, map->b2, DI_DISP3_B2_MAP); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mx3fb->lock, lock_flags); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "DI_DISP_IF_CONF = 0x%08X\n", 6158c2ecf20Sopenharmony_ci mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF)); 6168c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "DI_DISP_SIG_POL = 0x%08X\n", 6178c2ecf20Sopenharmony_ci mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL)); 6188c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "DI_DISP3_TIME_CONF = 0x%08X\n", 6198c2ecf20Sopenharmony_ci mx3fb_read_reg(mx3fb, DI_DISP3_TIME_CONF)); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci/** 6258c2ecf20Sopenharmony_ci * sdc_set_color_key() - set the transparent color key for SDC graphic plane. 6268c2ecf20Sopenharmony_ci * @mx3fb: mx3fb context. 6278c2ecf20Sopenharmony_ci * @channel: IPU DMAC channel ID. 6288c2ecf20Sopenharmony_ci * @enable: boolean to enable or disable color keyl. 6298c2ecf20Sopenharmony_ci * @color_key: 24-bit RGB color to use as transparent color key. 6308c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_cistatic int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel, 6338c2ecf20Sopenharmony_ci bool enable, uint32_t color_key) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci uint32_t reg, sdc_conf; 6368c2ecf20Sopenharmony_ci unsigned long lock_flags; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irqsave(&mx3fb->lock, lock_flags); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF); 6418c2ecf20Sopenharmony_ci if (channel == IDMAC_SDC_0) 6428c2ecf20Sopenharmony_ci sdc_conf &= ~SDC_COM_GWSEL; 6438c2ecf20Sopenharmony_ci else 6448c2ecf20Sopenharmony_ci sdc_conf |= SDC_COM_GWSEL; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (enable) { 6478c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L; 6488c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg | (color_key & 0x00FFFFFFL), 6498c2ecf20Sopenharmony_ci SDC_GW_CTRL); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci sdc_conf |= SDC_COM_KEY_COLOR_G; 6528c2ecf20Sopenharmony_ci } else { 6538c2ecf20Sopenharmony_ci sdc_conf &= ~SDC_COM_KEY_COLOR_G; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, sdc_conf, SDC_COM_CONF); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mx3fb->lock, lock_flags); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * sdc_set_global_alpha() - set global alpha blending modes. 6648c2ecf20Sopenharmony_ci * @mx3fb: mx3fb context. 6658c2ecf20Sopenharmony_ci * @enable: boolean to enable or disable global alpha blending. If disabled, 6668c2ecf20Sopenharmony_ci * per pixel blending is used. 6678c2ecf20Sopenharmony_ci * @alpha: global alpha value. 6688c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t alpha) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci uint32_t reg; 6738c2ecf20Sopenharmony_ci unsigned long lock_flags; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci spin_lock_irqsave(&mx3fb->lock, lock_flags); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (enable) { 6788c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0x00FFFFFFL; 6798c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg | ((uint32_t) alpha << 24), SDC_GW_CTRL); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); 6828c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg | SDC_COM_GLB_A, SDC_COM_CONF); 6838c2ecf20Sopenharmony_ci } else { 6848c2ecf20Sopenharmony_ci reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); 6858c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, reg & ~SDC_COM_GLB_A, SDC_COM_CONF); 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mx3fb->lock, lock_flags); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic u32 sdc_get_brightness(struct mx3fb_data *mx3fb) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci u32 brightness; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci brightness = mx3fb_read_reg(mx3fb, SDC_PWM_CTRL); 6988c2ecf20Sopenharmony_ci brightness = (brightness >> 16) & 0xFF; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return brightness; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "%s: value = %d\n", __func__, value); 7068c2ecf20Sopenharmony_ci /* This might be board-specific */ 7078c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); 7088c2ecf20Sopenharmony_ci return; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic uint32_t bpp_to_pixfmt(int bpp) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci uint32_t pixfmt = 0; 7148c2ecf20Sopenharmony_ci switch (bpp) { 7158c2ecf20Sopenharmony_ci case 24: 7168c2ecf20Sopenharmony_ci pixfmt = IPU_PIX_FMT_BGR24; 7178c2ecf20Sopenharmony_ci break; 7188c2ecf20Sopenharmony_ci case 32: 7198c2ecf20Sopenharmony_ci pixfmt = IPU_PIX_FMT_BGR32; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci case 16: 7228c2ecf20Sopenharmony_ci pixfmt = IPU_PIX_FMT_RGB565; 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci return pixfmt; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic int mx3fb_blank(int blank, struct fb_info *fbi); 7298c2ecf20Sopenharmony_cistatic int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, 7308c2ecf20Sopenharmony_ci bool lock); 7318c2ecf20Sopenharmony_cistatic int mx3fb_unmap_video_memory(struct fb_info *fbi); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/** 7348c2ecf20Sopenharmony_ci * mx3fb_set_fix() - set fixed framebuffer parameters from variable settings. 7358c2ecf20Sopenharmony_ci * @info: framebuffer information pointer 7368c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 7378c2ecf20Sopenharmony_ci */ 7388c2ecf20Sopenharmony_cistatic int mx3fb_set_fix(struct fb_info *fbi) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 7418c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci strncpy(fix->id, "DISP3 BG", 8); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 7488c2ecf20Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 7498c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 7508c2ecf20Sopenharmony_ci fix->xpanstep = 1; 7518c2ecf20Sopenharmony_ci fix->ypanstep = 1; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic void mx3fb_dma_done(void *arg) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct idmac_tx_desc *tx_desc = to_tx_desc(arg); 7598c2ecf20Sopenharmony_ci struct dma_chan *chan = tx_desc->txd.chan; 7608c2ecf20Sopenharmony_ci struct idmac_channel *ichannel = to_idmac_chan(chan); 7618c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = ichannel->client; 7628c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* We only need one interrupt, it will be re-enabled as needed */ 7678c2ecf20Sopenharmony_ci disable_irq_nosync(ichannel->eof_irq); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci complete(&mx3_fbi->flip_cmpl); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic bool mx3fb_must_set_par(struct fb_info *fbi) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 7758c2ecf20Sopenharmony_ci struct fb_var_screeninfo old_var = mx3_fbi->cur_var; 7768c2ecf20Sopenharmony_ci struct fb_var_screeninfo new_var = fbi->var; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if ((fbi->var.activate & FB_ACTIVATE_FORCE) && 7798c2ecf20Sopenharmony_ci (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) 7808c2ecf20Sopenharmony_ci return true; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* 7838c2ecf20Sopenharmony_ci * Ignore xoffset and yoffset update, 7848c2ecf20Sopenharmony_ci * because pan display handles this case. 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci old_var.xoffset = new_var.xoffset; 7878c2ecf20Sopenharmony_ci old_var.yoffset = new_var.yoffset; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo)); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int __set_par(struct fb_info *fbi, bool lock) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci u32 mem_len, cur_xoffset, cur_yoffset; 7958c2ecf20Sopenharmony_ci struct ipu_di_signal_cfg sig_cfg; 7968c2ecf20Sopenharmony_ci enum ipu_panel mode = IPU_PANEL_TFT; 7978c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 7988c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; 7998c2ecf20Sopenharmony_ci struct idmac_channel *ichan = mx3_fbi->idmac_channel; 8008c2ecf20Sopenharmony_ci struct idmac_video_param *video = &ichan->params.video; 8018c2ecf20Sopenharmony_ci struct scatterlist *sg = mx3_fbi->sg; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* Total cleanup */ 8048c2ecf20Sopenharmony_ci if (mx3_fbi->txd) 8058c2ecf20Sopenharmony_ci sdc_disable_channel(mx3_fbi); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci mx3fb_set_fix(fbi); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci mem_len = fbi->var.yres_virtual * fbi->fix.line_length; 8108c2ecf20Sopenharmony_ci if (mem_len > fbi->fix.smem_len) { 8118c2ecf20Sopenharmony_ci if (fbi->fix.smem_start) 8128c2ecf20Sopenharmony_ci mx3fb_unmap_video_memory(fbi); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (mx3fb_map_video_memory(fbi, mem_len, lock) < 0) 8158c2ecf20Sopenharmony_ci return -ENOMEM; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci sg_init_table(&sg[0], 1); 8198c2ecf20Sopenharmony_ci sg_init_table(&sg[1], 1); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci sg_dma_address(&sg[0]) = fbi->fix.smem_start; 8228c2ecf20Sopenharmony_ci sg_set_page(&sg[0], virt_to_page(fbi->screen_base), 8238c2ecf20Sopenharmony_ci fbi->fix.smem_len, 8248c2ecf20Sopenharmony_ci offset_in_page(fbi->screen_base)); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (mx3_fbi->ipu_ch == IDMAC_SDC_0) { 8278c2ecf20Sopenharmony_ci memset(&sig_cfg, 0, sizeof(sig_cfg)); 8288c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) 8298c2ecf20Sopenharmony_ci sig_cfg.Hsync_pol = true; 8308c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) 8318c2ecf20Sopenharmony_ci sig_cfg.Vsync_pol = true; 8328c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_CLK_INVERT) 8338c2ecf20Sopenharmony_ci sig_cfg.clk_pol = true; 8348c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_DATA_INVERT) 8358c2ecf20Sopenharmony_ci sig_cfg.data_pol = true; 8368c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH) 8378c2ecf20Sopenharmony_ci sig_cfg.enable_pol = true; 8388c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) 8398c2ecf20Sopenharmony_ci sig_cfg.clkidle_en = true; 8408c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_CLK_SEL_EN) 8418c2ecf20Sopenharmony_ci sig_cfg.clksel_en = true; 8428c2ecf20Sopenharmony_ci if (fbi->var.sync & FB_SYNC_SHARP_MODE) 8438c2ecf20Sopenharmony_ci mode = IPU_PANEL_SHARP_TFT; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "pixclock = %u Hz\n", 8468c2ecf20Sopenharmony_ci (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (sdc_init_panel(mx3fb, mode, 8498c2ecf20Sopenharmony_ci (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, 8508c2ecf20Sopenharmony_ci fbi->var.xres, fbi->var.yres, 8518c2ecf20Sopenharmony_ci fbi->var.left_margin, 8528c2ecf20Sopenharmony_ci fbi->var.hsync_len, 8538c2ecf20Sopenharmony_ci fbi->var.right_margin + 8548c2ecf20Sopenharmony_ci fbi->var.hsync_len, 8558c2ecf20Sopenharmony_ci fbi->var.upper_margin, 8568c2ecf20Sopenharmony_ci fbi->var.vsync_len, 8578c2ecf20Sopenharmony_ci fbi->var.lower_margin + 8588c2ecf20Sopenharmony_ci fbi->var.vsync_len, &sig_cfg) != 0) { 8598c2ecf20Sopenharmony_ci dev_err(fbi->device, 8608c2ecf20Sopenharmony_ci "mx3fb: Error initializing panel.\n"); 8618c2ecf20Sopenharmony_ci return -EINVAL; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, 0, 0); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci mx3_fbi->cur_ipu_buf = 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci video->out_pixel_fmt = bpp_to_pixfmt(fbi->var.bits_per_pixel); 8708c2ecf20Sopenharmony_ci video->out_width = fbi->var.xres; 8718c2ecf20Sopenharmony_ci video->out_height = fbi->var.yres; 8728c2ecf20Sopenharmony_ci video->out_stride = fbi->var.xres_virtual; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (mx3_fbi->blank == FB_BLANK_UNBLANK) { 8758c2ecf20Sopenharmony_ci sdc_enable_channel(mx3_fbi); 8768c2ecf20Sopenharmony_ci /* 8778c2ecf20Sopenharmony_ci * sg[0] points to fb smem_start address 8788c2ecf20Sopenharmony_ci * and is actually active in controller. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ci mx3_fbi->cur_var.xoffset = 0; 8818c2ecf20Sopenharmony_ci mx3_fbi->cur_var.yoffset = 0; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * Preserve xoffset and yoffest in case they are 8868c2ecf20Sopenharmony_ci * inactive in controller as fb is blanked. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ci cur_xoffset = mx3_fbi->cur_var.xoffset; 8898c2ecf20Sopenharmony_ci cur_yoffset = mx3_fbi->cur_var.yoffset; 8908c2ecf20Sopenharmony_ci mx3_fbi->cur_var = fbi->var; 8918c2ecf20Sopenharmony_ci mx3_fbi->cur_var.xoffset = cur_xoffset; 8928c2ecf20Sopenharmony_ci mx3_fbi->cur_var.yoffset = cur_yoffset; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci return 0; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci/** 8988c2ecf20Sopenharmony_ci * mx3fb_set_par() - set framebuffer parameters and change the operating mode. 8998c2ecf20Sopenharmony_ci * @fbi: framebuffer information pointer. 9008c2ecf20Sopenharmony_ci * @return: 0 on success or negative error code on failure. 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic int mx3fb_set_par(struct fb_info *fbi) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 9058c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; 9068c2ecf20Sopenharmony_ci struct idmac_channel *ichan = mx3_fbi->idmac_channel; 9078c2ecf20Sopenharmony_ci int ret; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+'); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci mutex_lock(&mx3_fbi->mutex); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return ret; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/** 9218c2ecf20Sopenharmony_ci * mx3fb_check_var() - check and adjust framebuffer variable parameters. 9228c2ecf20Sopenharmony_ci * @var: framebuffer variable parameters 9238c2ecf20Sopenharmony_ci * @fbi: framebuffer information pointer 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_cistatic int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 9288c2ecf20Sopenharmony_ci u32 vtotal; 9298c2ecf20Sopenharmony_ci u32 htotal; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "%s\n", __func__); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xres) 9348c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 9358c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yres) 9368c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && 9398c2ecf20Sopenharmony_ci (var->bits_per_pixel != 16)) 9408c2ecf20Sopenharmony_ci var->bits_per_pixel = default_bpp; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 9438c2ecf20Sopenharmony_ci case 16: 9448c2ecf20Sopenharmony_ci var->red.length = 5; 9458c2ecf20Sopenharmony_ci var->red.offset = 11; 9468c2ecf20Sopenharmony_ci var->red.msb_right = 0; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci var->green.length = 6; 9498c2ecf20Sopenharmony_ci var->green.offset = 5; 9508c2ecf20Sopenharmony_ci var->green.msb_right = 0; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci var->blue.length = 5; 9538c2ecf20Sopenharmony_ci var->blue.offset = 0; 9548c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci var->transp.length = 0; 9578c2ecf20Sopenharmony_ci var->transp.offset = 0; 9588c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci case 24: 9618c2ecf20Sopenharmony_ci var->red.length = 8; 9628c2ecf20Sopenharmony_ci var->red.offset = 16; 9638c2ecf20Sopenharmony_ci var->red.msb_right = 0; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci var->green.length = 8; 9668c2ecf20Sopenharmony_ci var->green.offset = 8; 9678c2ecf20Sopenharmony_ci var->green.msb_right = 0; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci var->blue.length = 8; 9708c2ecf20Sopenharmony_ci var->blue.offset = 0; 9718c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci var->transp.length = 0; 9748c2ecf20Sopenharmony_ci var->transp.offset = 0; 9758c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 9768c2ecf20Sopenharmony_ci break; 9778c2ecf20Sopenharmony_ci case 32: 9788c2ecf20Sopenharmony_ci var->red.length = 8; 9798c2ecf20Sopenharmony_ci var->red.offset = 16; 9808c2ecf20Sopenharmony_ci var->red.msb_right = 0; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci var->green.length = 8; 9838c2ecf20Sopenharmony_ci var->green.offset = 8; 9848c2ecf20Sopenharmony_ci var->green.msb_right = 0; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci var->blue.length = 8; 9878c2ecf20Sopenharmony_ci var->blue.offset = 0; 9888c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci var->transp.length = 8; 9918c2ecf20Sopenharmony_ci var->transp.offset = 24; 9928c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 9938c2ecf20Sopenharmony_ci break; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (var->pixclock < 1000) { 9978c2ecf20Sopenharmony_ci htotal = var->xres + var->right_margin + var->hsync_len + 9988c2ecf20Sopenharmony_ci var->left_margin; 9998c2ecf20Sopenharmony_ci vtotal = var->yres + var->lower_margin + var->vsync_len + 10008c2ecf20Sopenharmony_ci var->upper_margin; 10018c2ecf20Sopenharmony_ci var->pixclock = (vtotal * htotal * 6UL) / 100UL; 10028c2ecf20Sopenharmony_ci var->pixclock = KHZ2PICOS(var->pixclock); 10038c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "pixclock set for 60Hz refresh = %u ps\n", 10048c2ecf20Sopenharmony_ci var->pixclock); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci var->height = -1; 10088c2ecf20Sopenharmony_ci var->width = -1; 10098c2ecf20Sopenharmony_ci var->grayscale = 0; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* Preserve sync flags */ 10128c2ecf20Sopenharmony_ci var->sync |= mx3_fbi->cur_var.sync; 10138c2ecf20Sopenharmony_ci mx3_fbi->cur_var.sync |= var->sync; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic u32 chan_to_field(unsigned int chan, struct fb_bitfield *bf) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci chan &= 0xffff; 10218c2ecf20Sopenharmony_ci chan >>= 16 - bf->length; 10228c2ecf20Sopenharmony_ci return chan << bf->offset; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int mx3fb_setcolreg(unsigned int regno, unsigned int red, 10268c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 10278c2ecf20Sopenharmony_ci unsigned int trans, struct fb_info *fbi) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 10308c2ecf20Sopenharmony_ci u32 val; 10318c2ecf20Sopenharmony_ci int ret = 1; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "%s, regno = %u\n", __func__, regno); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci mutex_lock(&mx3_fbi->mutex); 10368c2ecf20Sopenharmony_ci /* 10378c2ecf20Sopenharmony_ci * If greyscale is true, then we convert the RGB value 10388c2ecf20Sopenharmony_ci * to greyscale no matter what visual we are using. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci if (fbi->var.grayscale) 10418c2ecf20Sopenharmony_ci red = green = blue = (19595 * red + 38470 * green + 10428c2ecf20Sopenharmony_ci 7471 * blue) >> 16; 10438c2ecf20Sopenharmony_ci switch (fbi->fix.visual) { 10448c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * 16-bit True Colour. We encode the RGB value 10478c2ecf20Sopenharmony_ci * according to the RGB bitfield information. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (regno < 16) { 10508c2ecf20Sopenharmony_ci u32 *pal = fbi->pseudo_palette; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci val = chan_to_field(red, &fbi->var.red); 10538c2ecf20Sopenharmony_ci val |= chan_to_field(green, &fbi->var.green); 10548c2ecf20Sopenharmony_ci val |= chan_to_field(blue, &fbi->var.blue); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci pal[regno] = val; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci ret = 0; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci case FB_VISUAL_STATIC_PSEUDOCOLOR: 10638c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci return ret; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic void __blank(int blank, struct fb_info *fbi) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 10748c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; 10758c2ecf20Sopenharmony_ci int was_blank = mx3_fbi->blank; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci mx3_fbi->blank = blank; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* Attention! 10808c2ecf20Sopenharmony_ci * Do not call sdc_disable_channel() for a channel that is disabled 10818c2ecf20Sopenharmony_ci * already! This will result in a kernel NULL pointer dereference 10828c2ecf20Sopenharmony_ci * (mx3_fbi->txd is NULL). Hide the fact, that all blank modes are 10838c2ecf20Sopenharmony_ci * handled equally by this driver. 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_ci if (blank > FB_BLANK_UNBLANK && was_blank > FB_BLANK_UNBLANK) 10868c2ecf20Sopenharmony_ci return; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci switch (blank) { 10898c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 10908c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 10918c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 10928c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 10938c2ecf20Sopenharmony_ci sdc_set_brightness(mx3fb, 0); 10948c2ecf20Sopenharmony_ci memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); 10958c2ecf20Sopenharmony_ci /* Give LCD time to update - enough for 50 and 60 Hz */ 10968c2ecf20Sopenharmony_ci msleep(25); 10978c2ecf20Sopenharmony_ci sdc_disable_channel(mx3_fbi); 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 11008c2ecf20Sopenharmony_ci sdc_enable_channel(mx3_fbi); 11018c2ecf20Sopenharmony_ci sdc_set_brightness(mx3fb, mx3fb->backlight_level); 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci/** 11078c2ecf20Sopenharmony_ci * mx3fb_blank() - blank the display. 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_cistatic int mx3fb_blank(int blank, struct fb_info *fbi) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "%s, blank = %d, base %p, len %u\n", __func__, 11148c2ecf20Sopenharmony_ci blank, fbi->screen_base, fbi->fix.smem_len); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (mx3_fbi->blank == blank) 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mutex_lock(&mx3_fbi->mutex); 11208c2ecf20Sopenharmony_ci __blank(blank, fbi); 11218c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci return 0; 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci/** 11278c2ecf20Sopenharmony_ci * mx3fb_pan_display() - pan or wrap the display 11288c2ecf20Sopenharmony_ci * @var: variable screen buffer information. 11298c2ecf20Sopenharmony_ci * @info: framebuffer information pointer. 11308c2ecf20Sopenharmony_ci * 11318c2ecf20Sopenharmony_ci * We look only at xoffset, yoffset and the FB_VMODE_YWRAP flag 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_cistatic int mx3fb_pan_display(struct fb_var_screeninfo *var, 11348c2ecf20Sopenharmony_ci struct fb_info *fbi) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 11378c2ecf20Sopenharmony_ci u32 y_bottom; 11388c2ecf20Sopenharmony_ci unsigned long base; 11398c2ecf20Sopenharmony_ci off_t offset; 11408c2ecf20Sopenharmony_ci dma_cookie_t cookie; 11418c2ecf20Sopenharmony_ci struct scatterlist *sg = mx3_fbi->sg; 11428c2ecf20Sopenharmony_ci struct dma_chan *dma_chan = &mx3_fbi->idmac_channel->dma_chan; 11438c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *txd; 11448c2ecf20Sopenharmony_ci int ret; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "%s [%c]\n", __func__, 11478c2ecf20Sopenharmony_ci list_empty(&mx3_fbi->idmac_channel->queue) ? '-' : '+'); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (var->xoffset > 0) { 11508c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "x panning not supported\n"); 11518c2ecf20Sopenharmony_ci return -EINVAL; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (mx3_fbi->cur_var.xoffset == var->xoffset && 11558c2ecf20Sopenharmony_ci mx3_fbi->cur_var.yoffset == var->yoffset) 11568c2ecf20Sopenharmony_ci return 0; /* No change, do nothing */ 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci y_bottom = var->yoffset; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (!(var->vmode & FB_VMODE_YWRAP)) 11618c2ecf20Sopenharmony_ci y_bottom += fbi->var.yres; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (y_bottom > fbi->var.yres_virtual) 11648c2ecf20Sopenharmony_ci return -EINVAL; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci mutex_lock(&mx3_fbi->mutex); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci offset = var->yoffset * fbi->fix.line_length 11698c2ecf20Sopenharmony_ci + var->xoffset * (fbi->var.bits_per_pixel / 8); 11708c2ecf20Sopenharmony_ci base = fbi->fix.smem_start + offset; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n", 11738c2ecf20Sopenharmony_ci mx3_fbi->cur_ipu_buf, base); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* 11768c2ecf20Sopenharmony_ci * We enable the End of Frame interrupt, which will free a tx-descriptor, 11778c2ecf20Sopenharmony_ci * which we will need for the next dmaengine_prep_slave_sg(). The 11788c2ecf20Sopenharmony_ci * IRQ-handler will disable the IRQ again. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_ci init_completion(&mx3_fbi->flip_cmpl); 11818c2ecf20Sopenharmony_ci enable_irq(mx3_fbi->idmac_channel->eof_irq); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&mx3_fbi->flip_cmpl, HZ / 10); 11848c2ecf20Sopenharmony_ci if (ret <= 0) { 11858c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 11868c2ecf20Sopenharmony_ci dev_info(fbi->device, "Panning failed due to %s\n", ret < 0 ? 11878c2ecf20Sopenharmony_ci "user interrupt" : "timeout"); 11888c2ecf20Sopenharmony_ci disable_irq(mx3_fbi->idmac_channel->eof_irq); 11898c2ecf20Sopenharmony_ci return ret ? : -ETIMEDOUT; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci mx3_fbi->cur_ipu_buf = !mx3_fbi->cur_ipu_buf; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci sg_dma_address(&sg[mx3_fbi->cur_ipu_buf]) = base; 11958c2ecf20Sopenharmony_ci sg_set_page(&sg[mx3_fbi->cur_ipu_buf], 11968c2ecf20Sopenharmony_ci virt_to_page(fbi->screen_base + offset), fbi->fix.smem_len, 11978c2ecf20Sopenharmony_ci offset_in_page(fbi->screen_base + offset)); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (mx3_fbi->txd) 12008c2ecf20Sopenharmony_ci async_tx_ack(mx3_fbi->txd); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci txd = dmaengine_prep_slave_sg(dma_chan, sg + 12038c2ecf20Sopenharmony_ci mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); 12048c2ecf20Sopenharmony_ci if (!txd) { 12058c2ecf20Sopenharmony_ci dev_err(fbi->device, 12068c2ecf20Sopenharmony_ci "Error preparing a DMA transaction descriptor.\n"); 12078c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 12088c2ecf20Sopenharmony_ci return -EIO; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci txd->callback_param = txd; 12128c2ecf20Sopenharmony_ci txd->callback = mx3fb_dma_done; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* 12158c2ecf20Sopenharmony_ci * Emulate original mx3fb behaviour: each new call to idmac_tx_submit() 12168c2ecf20Sopenharmony_ci * should switch to another buffer 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci cookie = txd->tx_submit(txd); 12198c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "%d: Submit %p #%d\n", __LINE__, txd, cookie); 12208c2ecf20Sopenharmony_ci if (cookie < 0) { 12218c2ecf20Sopenharmony_ci dev_err(fbi->device, 12228c2ecf20Sopenharmony_ci "Error updating SDC buf %d to address=0x%08lX\n", 12238c2ecf20Sopenharmony_ci mx3_fbi->cur_ipu_buf, base); 12248c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 12258c2ecf20Sopenharmony_ci return -EIO; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci mx3_fbi->txd = txd; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci fbi->var.xoffset = var->xoffset; 12318c2ecf20Sopenharmony_ci fbi->var.yoffset = var->yoffset; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) 12348c2ecf20Sopenharmony_ci fbi->var.vmode |= FB_VMODE_YWRAP; 12358c2ecf20Sopenharmony_ci else 12368c2ecf20Sopenharmony_ci fbi->var.vmode &= ~FB_VMODE_YWRAP; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci mx3_fbi->cur_var = fbi->var; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci mutex_unlock(&mx3_fbi->mutex); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "Update complete\n"); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci return 0; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci/* 12488c2ecf20Sopenharmony_ci * This structure contains the pointers to the control functions that are 12498c2ecf20Sopenharmony_ci * invoked by the core framebuffer driver to perform operations like 12508c2ecf20Sopenharmony_ci * blitting, rectangle filling, copy regions and cursor definition. 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_cistatic const struct fb_ops mx3fb_ops = { 12538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12548c2ecf20Sopenharmony_ci .fb_set_par = mx3fb_set_par, 12558c2ecf20Sopenharmony_ci .fb_check_var = mx3fb_check_var, 12568c2ecf20Sopenharmony_ci .fb_setcolreg = mx3fb_setcolreg, 12578c2ecf20Sopenharmony_ci .fb_pan_display = mx3fb_pan_display, 12588c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 12598c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 12608c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 12618c2ecf20Sopenharmony_ci .fb_blank = mx3fb_blank, 12628c2ecf20Sopenharmony_ci}; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12658c2ecf20Sopenharmony_ci/* 12668c2ecf20Sopenharmony_ci * Power management hooks. Note that we won't be called from IRQ context, 12678c2ecf20Sopenharmony_ci * unlike the blank functions above, so we may sleep. 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* 12718c2ecf20Sopenharmony_ci * Suspends the framebuffer and blanks the screen. Power management support 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic int mx3fb_suspend(struct platform_device *pdev, pm_message_t state) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); 12768c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci console_lock(); 12798c2ecf20Sopenharmony_ci fb_set_suspend(mx3fb->fbi, 1); 12808c2ecf20Sopenharmony_ci console_unlock(); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (mx3_fbi->blank == FB_BLANK_UNBLANK) { 12838c2ecf20Sopenharmony_ci sdc_disable_channel(mx3_fbi); 12848c2ecf20Sopenharmony_ci sdc_set_brightness(mx3fb, 0); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci return 0; 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci/* 12918c2ecf20Sopenharmony_ci * Resumes the framebuffer and unblanks the screen. Power management support 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_cistatic int mx3fb_resume(struct platform_device *pdev) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); 12968c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (mx3_fbi->blank == FB_BLANK_UNBLANK) { 12998c2ecf20Sopenharmony_ci sdc_enable_channel(mx3_fbi); 13008c2ecf20Sopenharmony_ci sdc_set_brightness(mx3fb, mx3fb->backlight_level); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci console_lock(); 13048c2ecf20Sopenharmony_ci fb_set_suspend(mx3fb->fbi, 0); 13058c2ecf20Sopenharmony_ci console_unlock(); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return 0; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci#else 13108c2ecf20Sopenharmony_ci#define mx3fb_suspend NULL 13118c2ecf20Sopenharmony_ci#define mx3fb_resume NULL 13128c2ecf20Sopenharmony_ci#endif 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci/* 13158c2ecf20Sopenharmony_ci * Main framebuffer functions 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci/** 13198c2ecf20Sopenharmony_ci * mx3fb_map_video_memory() - allocates the DRAM memory for the frame buffer. 13208c2ecf20Sopenharmony_ci * @fbi: framebuffer information pointer 13218c2ecf20Sopenharmony_ci * @mem_len: length of mapped memory 13228c2ecf20Sopenharmony_ci * @lock: do not lock during initialisation 13238c2ecf20Sopenharmony_ci * @return: Error code indicating success or failure 13248c2ecf20Sopenharmony_ci * 13258c2ecf20Sopenharmony_ci * This buffer is remapped into a non-cached, non-buffered, memory region to 13268c2ecf20Sopenharmony_ci * allow palette and pixel writes to occur without flushing the cache. Once this 13278c2ecf20Sopenharmony_ci * area is remapped, all virtual memory access to the video memory should occur 13288c2ecf20Sopenharmony_ci * at the new region. 13298c2ecf20Sopenharmony_ci */ 13308c2ecf20Sopenharmony_cistatic int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, 13318c2ecf20Sopenharmony_ci bool lock) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci int retval = 0; 13348c2ecf20Sopenharmony_ci dma_addr_t addr; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci fbi->screen_base = dma_alloc_wc(fbi->device, mem_len, &addr, 13378c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (!fbi->screen_base) { 13408c2ecf20Sopenharmony_ci dev_err(fbi->device, "Cannot allocate %u bytes framebuffer memory\n", 13418c2ecf20Sopenharmony_ci mem_len); 13428c2ecf20Sopenharmony_ci retval = -EBUSY; 13438c2ecf20Sopenharmony_ci goto err0; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci if (lock) 13478c2ecf20Sopenharmony_ci mutex_lock(&fbi->mm_lock); 13488c2ecf20Sopenharmony_ci fbi->fix.smem_start = addr; 13498c2ecf20Sopenharmony_ci fbi->fix.smem_len = mem_len; 13508c2ecf20Sopenharmony_ci if (lock) 13518c2ecf20Sopenharmony_ci mutex_unlock(&fbi->mm_lock); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n", 13548c2ecf20Sopenharmony_ci (uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci fbi->screen_size = fbi->fix.smem_len; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* Clear the screen */ 13598c2ecf20Sopenharmony_ci memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci return 0; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cierr0: 13648c2ecf20Sopenharmony_ci fbi->fix.smem_len = 0; 13658c2ecf20Sopenharmony_ci fbi->fix.smem_start = 0; 13668c2ecf20Sopenharmony_ci fbi->screen_base = NULL; 13678c2ecf20Sopenharmony_ci return retval; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci/** 13718c2ecf20Sopenharmony_ci * mx3fb_unmap_video_memory() - de-allocate frame buffer memory. 13728c2ecf20Sopenharmony_ci * @fbi: framebuffer information pointer 13738c2ecf20Sopenharmony_ci * @return: error code indicating success or failure 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_cistatic int mx3fb_unmap_video_memory(struct fb_info *fbi) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci dma_free_wc(fbi->device, fbi->fix.smem_len, fbi->screen_base, 13788c2ecf20Sopenharmony_ci fbi->fix.smem_start); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci fbi->screen_base = NULL; 13818c2ecf20Sopenharmony_ci mutex_lock(&fbi->mm_lock); 13828c2ecf20Sopenharmony_ci fbi->fix.smem_start = 0; 13838c2ecf20Sopenharmony_ci fbi->fix.smem_len = 0; 13848c2ecf20Sopenharmony_ci mutex_unlock(&fbi->mm_lock); 13858c2ecf20Sopenharmony_ci return 0; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci/** 13898c2ecf20Sopenharmony_ci * mx3fb_init_fbinfo() - initialize framebuffer information object. 13908c2ecf20Sopenharmony_ci * @return: initialized framebuffer structure. 13918c2ecf20Sopenharmony_ci */ 13928c2ecf20Sopenharmony_cistatic struct fb_info *mx3fb_init_fbinfo(struct device *dev, 13938c2ecf20Sopenharmony_ci const struct fb_ops *ops) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct fb_info *fbi; 13968c2ecf20Sopenharmony_ci struct mx3fb_info *mx3fbi; 13978c2ecf20Sopenharmony_ci int ret; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Allocate sufficient memory for the fb structure */ 14008c2ecf20Sopenharmony_ci fbi = framebuffer_alloc(sizeof(struct mx3fb_info), dev); 14018c2ecf20Sopenharmony_ci if (!fbi) 14028c2ecf20Sopenharmony_ci return NULL; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci mx3fbi = fbi->par; 14058c2ecf20Sopenharmony_ci mx3fbi->cookie = -EINVAL; 14068c2ecf20Sopenharmony_ci mx3fbi->cur_ipu_buf = 0; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci fbi->var.activate = FB_ACTIVATE_NOW; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci fbi->fbops = ops; 14118c2ecf20Sopenharmony_ci fbi->flags = FBINFO_FLAG_DEFAULT; 14128c2ecf20Sopenharmony_ci fbi->pseudo_palette = mx3fbi->pseudo_palette; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci mutex_init(&mx3fbi->mutex); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* Allocate colormap */ 14178c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&fbi->cmap, 16, 0); 14188c2ecf20Sopenharmony_ci if (ret < 0) { 14198c2ecf20Sopenharmony_ci framebuffer_release(fbi); 14208c2ecf20Sopenharmony_ci return NULL; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci return fbi; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci struct device *dev = mx3fb->dev; 14298c2ecf20Sopenharmony_ci struct mx3fb_platform_data *mx3fb_pdata = dev_get_platdata(dev); 14308c2ecf20Sopenharmony_ci const char *name = mx3fb_pdata->name; 14318c2ecf20Sopenharmony_ci unsigned int irq; 14328c2ecf20Sopenharmony_ci struct fb_info *fbi; 14338c2ecf20Sopenharmony_ci struct mx3fb_info *mx3fbi; 14348c2ecf20Sopenharmony_ci const struct fb_videomode *mode; 14358c2ecf20Sopenharmony_ci int ret, num_modes; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) { 14388c2ecf20Sopenharmony_ci dev_err(dev, "Illegal display data format %d\n", 14398c2ecf20Sopenharmony_ci mx3fb_pdata->disp_data_fmt); 14408c2ecf20Sopenharmony_ci return -EINVAL; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci ichan->client = mx3fb; 14448c2ecf20Sopenharmony_ci irq = ichan->eof_irq; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (ichan->dma_chan.chan_id != IDMAC_SDC_0) 14478c2ecf20Sopenharmony_ci return -EINVAL; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops); 14508c2ecf20Sopenharmony_ci if (!fbi) 14518c2ecf20Sopenharmony_ci return -ENOMEM; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (!fb_mode) 14548c2ecf20Sopenharmony_ci fb_mode = name; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (!fb_mode) { 14578c2ecf20Sopenharmony_ci ret = -EINVAL; 14588c2ecf20Sopenharmony_ci goto emode; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) { 14628c2ecf20Sopenharmony_ci mode = mx3fb_pdata->mode; 14638c2ecf20Sopenharmony_ci num_modes = mx3fb_pdata->num_modes; 14648c2ecf20Sopenharmony_ci } else { 14658c2ecf20Sopenharmony_ci mode = mx3fb_modedb; 14668c2ecf20Sopenharmony_ci num_modes = ARRAY_SIZE(mx3fb_modedb); 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (!fb_find_mode(&fbi->var, fbi, fb_mode, mode, 14708c2ecf20Sopenharmony_ci num_modes, NULL, default_bpp)) { 14718c2ecf20Sopenharmony_ci ret = -EBUSY; 14728c2ecf20Sopenharmony_ci goto emode; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci fb_videomode_to_modelist(mode, num_modes, &fbi->modelist); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci /* Default Y virtual size is 2x panel size */ 14788c2ecf20Sopenharmony_ci fbi->var.yres_virtual = fbi->var.yres * 2; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci mx3fb->fbi = fbi; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* set Display Interface clock period */ 14838c2ecf20Sopenharmony_ci mx3fb_write_reg(mx3fb, 0x00100010L, DI_HSP_CLK_PER); 14848c2ecf20Sopenharmony_ci /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci sdc_set_brightness(mx3fb, 255); 14878c2ecf20Sopenharmony_ci sdc_set_global_alpha(mx3fb, true, 0xFF); 14888c2ecf20Sopenharmony_ci sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci mx3fbi = fbi->par; 14918c2ecf20Sopenharmony_ci mx3fbi->idmac_channel = ichan; 14928c2ecf20Sopenharmony_ci mx3fbi->ipu_ch = ichan->dma_chan.chan_id; 14938c2ecf20Sopenharmony_ci mx3fbi->mx3fb = mx3fb; 14948c2ecf20Sopenharmony_ci mx3fbi->blank = FB_BLANK_NORMAL; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci init_completion(&mx3fbi->flip_cmpl); 14998c2ecf20Sopenharmony_ci disable_irq(ichan->eof_irq); 15008c2ecf20Sopenharmony_ci dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); 15018c2ecf20Sopenharmony_ci ret = __set_par(fbi, false); 15028c2ecf20Sopenharmony_ci if (ret < 0) 15038c2ecf20Sopenharmony_ci goto esetpar; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci __blank(FB_BLANK_UNBLANK, fbi); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci dev_info(dev, "registered, using mode %s\n", fb_mode); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci ret = register_framebuffer(fbi); 15108c2ecf20Sopenharmony_ci if (ret < 0) 15118c2ecf20Sopenharmony_ci goto erfb; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return 0; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cierfb: 15168c2ecf20Sopenharmony_ciesetpar: 15178c2ecf20Sopenharmony_ciemode: 15188c2ecf20Sopenharmony_ci fb_dealloc_cmap(&fbi->cmap); 15198c2ecf20Sopenharmony_ci framebuffer_release(fbi); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return ret; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic bool chan_filter(struct dma_chan *chan, void *arg) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct dma_chan_request *rq = arg; 15278c2ecf20Sopenharmony_ci struct device *dev; 15288c2ecf20Sopenharmony_ci struct mx3fb_platform_data *mx3fb_pdata; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (!imx_dma_is_ipu(chan)) 15318c2ecf20Sopenharmony_ci return false; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci if (!rq) 15348c2ecf20Sopenharmony_ci return false; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci dev = rq->mx3fb->dev; 15378c2ecf20Sopenharmony_ci mx3fb_pdata = dev_get_platdata(dev); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci return rq->id == chan->chan_id && 15408c2ecf20Sopenharmony_ci mx3fb_pdata->dma_dev == chan->device->dev; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic void release_fbi(struct fb_info *fbi) 15448c2ecf20Sopenharmony_ci{ 15458c2ecf20Sopenharmony_ci mx3fb_unmap_video_memory(fbi); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci fb_dealloc_cmap(&fbi->cmap); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci unregister_framebuffer(fbi); 15508c2ecf20Sopenharmony_ci framebuffer_release(fbi); 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_cistatic int mx3fb_probe(struct platform_device *pdev) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15568c2ecf20Sopenharmony_ci int ret; 15578c2ecf20Sopenharmony_ci struct resource *sdc_reg; 15588c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb; 15598c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 15608c2ecf20Sopenharmony_ci struct dma_chan *chan; 15618c2ecf20Sopenharmony_ci struct dma_chan_request rq; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* 15648c2ecf20Sopenharmony_ci * Display Interface (DI) and Synchronous Display Controller (SDC) 15658c2ecf20Sopenharmony_ci * registers 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci sdc_reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15688c2ecf20Sopenharmony_ci if (!sdc_reg) 15698c2ecf20Sopenharmony_ci return -EINVAL; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci mx3fb = devm_kzalloc(&pdev->dev, sizeof(*mx3fb), GFP_KERNEL); 15728c2ecf20Sopenharmony_ci if (!mx3fb) 15738c2ecf20Sopenharmony_ci return -ENOMEM; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci spin_lock_init(&mx3fb->lock); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci mx3fb->reg_base = ioremap(sdc_reg->start, resource_size(sdc_reg)); 15788c2ecf20Sopenharmony_ci if (!mx3fb->reg_base) { 15798c2ecf20Sopenharmony_ci ret = -ENOMEM; 15808c2ecf20Sopenharmony_ci goto eremap; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci /* IDMAC interface */ 15868c2ecf20Sopenharmony_ci dmaengine_get(); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci mx3fb->dev = dev; 15898c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mx3fb); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci rq.mx3fb = mx3fb; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci dma_cap_zero(mask); 15948c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 15958c2ecf20Sopenharmony_ci dma_cap_set(DMA_PRIVATE, mask); 15968c2ecf20Sopenharmony_ci rq.id = IDMAC_SDC_0; 15978c2ecf20Sopenharmony_ci chan = dma_request_channel(mask, chan_filter, &rq); 15988c2ecf20Sopenharmony_ci if (!chan) { 15998c2ecf20Sopenharmony_ci ret = -EBUSY; 16008c2ecf20Sopenharmony_ci goto ersdc0; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci mx3fb->backlight_level = 255; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci ret = init_fb_chan(mx3fb, to_idmac_chan(chan)); 16068c2ecf20Sopenharmony_ci if (ret < 0) 16078c2ecf20Sopenharmony_ci goto eisdc0; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci mx3fb_init_backlight(mx3fb); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci return 0; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cieisdc0: 16148c2ecf20Sopenharmony_ci dma_release_channel(chan); 16158c2ecf20Sopenharmony_ciersdc0: 16168c2ecf20Sopenharmony_ci dmaengine_put(); 16178c2ecf20Sopenharmony_ci iounmap(mx3fb->reg_base); 16188c2ecf20Sopenharmony_cieremap: 16198c2ecf20Sopenharmony_ci dev_err(dev, "mx3fb: failed to register fb\n"); 16208c2ecf20Sopenharmony_ci return ret; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int mx3fb_remove(struct platform_device *dev) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci struct mx3fb_data *mx3fb = platform_get_drvdata(dev); 16268c2ecf20Sopenharmony_ci struct fb_info *fbi = mx3fb->fbi; 16278c2ecf20Sopenharmony_ci struct mx3fb_info *mx3_fbi = fbi->par; 16288c2ecf20Sopenharmony_ci struct dma_chan *chan; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci chan = &mx3_fbi->idmac_channel->dma_chan; 16318c2ecf20Sopenharmony_ci release_fbi(fbi); 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci mx3fb_exit_backlight(mx3fb); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci dma_release_channel(chan); 16368c2ecf20Sopenharmony_ci dmaengine_put(); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci iounmap(mx3fb->reg_base); 16398c2ecf20Sopenharmony_ci return 0; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic struct platform_driver mx3fb_driver = { 16438c2ecf20Sopenharmony_ci .driver = { 16448c2ecf20Sopenharmony_ci .name = MX3FB_NAME, 16458c2ecf20Sopenharmony_ci }, 16468c2ecf20Sopenharmony_ci .probe = mx3fb_probe, 16478c2ecf20Sopenharmony_ci .remove = mx3fb_remove, 16488c2ecf20Sopenharmony_ci .suspend = mx3fb_suspend, 16498c2ecf20Sopenharmony_ci .resume = mx3fb_resume, 16508c2ecf20Sopenharmony_ci}; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/* 16538c2ecf20Sopenharmony_ci * Parse user specified options (`video=mx3fb:') 16548c2ecf20Sopenharmony_ci * example: 16558c2ecf20Sopenharmony_ci * video=mx3fb:bpp=16 16568c2ecf20Sopenharmony_ci */ 16578c2ecf20Sopenharmony_cistatic int __init mx3fb_setup(void) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci#ifndef MODULE 16608c2ecf20Sopenharmony_ci char *opt, *options = NULL; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (fb_get_options("mx3fb", &options)) 16638c2ecf20Sopenharmony_ci return -ENODEV; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (!options || !*options) 16668c2ecf20Sopenharmony_ci return 0; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci while ((opt = strsep(&options, ",")) != NULL) { 16698c2ecf20Sopenharmony_ci if (!*opt) 16708c2ecf20Sopenharmony_ci continue; 16718c2ecf20Sopenharmony_ci if (!strncmp(opt, "bpp=", 4)) 16728c2ecf20Sopenharmony_ci default_bpp = simple_strtoul(opt + 4, NULL, 0); 16738c2ecf20Sopenharmony_ci else 16748c2ecf20Sopenharmony_ci fb_mode = opt; 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci#endif 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_cistatic int __init mx3fb_init(void) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci int ret = mx3fb_setup(); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci if (ret < 0) 16868c2ecf20Sopenharmony_ci return ret; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci ret = platform_driver_register(&mx3fb_driver); 16898c2ecf20Sopenharmony_ci return ret; 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic void __exit mx3fb_exit(void) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci platform_driver_unregister(&mx3fb_driver); 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cimodule_init(mx3fb_init); 16988c2ecf20Sopenharmony_cimodule_exit(mx3fb_exit); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 17018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MX3 framebuffer driver"); 17028c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" MX3FB_NAME); 17038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1704