18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SuperH Mobile LCDC Framebuffer 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008 Magnus Damm 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 88c2ecf20Sopenharmony_ci * for more details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/atomic.h> 128c2ecf20Sopenharmony_ci#include <linux/backlight.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/console.h> 158c2ecf20Sopenharmony_ci#include <linux/ctype.h> 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/fbcon.h> 198c2ecf20Sopenharmony_ci#include <linux/gpio.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/mm.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 308c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <video/sh_mobile_lcdc.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "sh_mobile_lcdcfb.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------------- 378c2ecf20Sopenharmony_ci * Overlay register definitions 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define LDBCR 0xb00 418c2ecf20Sopenharmony_ci#define LDBCR_UPC(n) (1 << ((n) + 16)) 428c2ecf20Sopenharmony_ci#define LDBCR_UPF(n) (1 << ((n) + 8)) 438c2ecf20Sopenharmony_ci#define LDBCR_UPD(n) (1 << ((n) + 0)) 448c2ecf20Sopenharmony_ci#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00) 458c2ecf20Sopenharmony_ci#define LDBBSIFR_EN (1 << 31) 468c2ecf20Sopenharmony_ci#define LDBBSIFR_VS (1 << 29) 478c2ecf20Sopenharmony_ci#define LDBBSIFR_BRSEL (1 << 28) 488c2ecf20Sopenharmony_ci#define LDBBSIFR_MX (1 << 27) 498c2ecf20Sopenharmony_ci#define LDBBSIFR_MY (1 << 26) 508c2ecf20Sopenharmony_ci#define LDBBSIFR_CV3 (3 << 24) 518c2ecf20Sopenharmony_ci#define LDBBSIFR_CV2 (2 << 24) 528c2ecf20Sopenharmony_ci#define LDBBSIFR_CV1 (1 << 24) 538c2ecf20Sopenharmony_ci#define LDBBSIFR_CV0 (0 << 24) 548c2ecf20Sopenharmony_ci#define LDBBSIFR_CV_MASK (3 << 24) 558c2ecf20Sopenharmony_ci#define LDBBSIFR_LAY_MASK (0xff << 16) 568c2ecf20Sopenharmony_ci#define LDBBSIFR_LAY_SHIFT 16 578c2ecf20Sopenharmony_ci#define LDBBSIFR_ROP3_MASK (0xff << 16) 588c2ecf20Sopenharmony_ci#define LDBBSIFR_ROP3_SHIFT 16 598c2ecf20Sopenharmony_ci#define LDBBSIFR_AL_PL8 (3 << 14) 608c2ecf20Sopenharmony_ci#define LDBBSIFR_AL_PL1 (2 << 14) 618c2ecf20Sopenharmony_ci#define LDBBSIFR_AL_PK (1 << 14) 628c2ecf20Sopenharmony_ci#define LDBBSIFR_AL_1 (0 << 14) 638c2ecf20Sopenharmony_ci#define LDBBSIFR_AL_MASK (3 << 14) 648c2ecf20Sopenharmony_ci#define LDBBSIFR_SWPL (1 << 10) 658c2ecf20Sopenharmony_ci#define LDBBSIFR_SWPW (1 << 9) 668c2ecf20Sopenharmony_ci#define LDBBSIFR_SWPB (1 << 8) 678c2ecf20Sopenharmony_ci#define LDBBSIFR_RY (1 << 7) 688c2ecf20Sopenharmony_ci#define LDBBSIFR_CHRR_420 (2 << 0) 698c2ecf20Sopenharmony_ci#define LDBBSIFR_CHRR_422 (1 << 0) 708c2ecf20Sopenharmony_ci#define LDBBSIFR_CHRR_444 (0 << 0) 718c2ecf20Sopenharmony_ci#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0) 728c2ecf20Sopenharmony_ci#define LDBBSIFR_RPKF_RGB16 (0x03 << 0) 738c2ecf20Sopenharmony_ci#define LDBBSIFR_RPKF_RGB24 (0x0b << 0) 748c2ecf20Sopenharmony_ci#define LDBBSIFR_RPKF_MASK (0x1f << 0) 758c2ecf20Sopenharmony_ci#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04) 768c2ecf20Sopenharmony_ci#define LDBBSSZR_BVSS_MASK (0xfff << 16) 778c2ecf20Sopenharmony_ci#define LDBBSSZR_BVSS_SHIFT 16 788c2ecf20Sopenharmony_ci#define LDBBSSZR_BHSS_MASK (0xfff << 0) 798c2ecf20Sopenharmony_ci#define LDBBSSZR_BHSS_SHIFT 0 808c2ecf20Sopenharmony_ci#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08) 818c2ecf20Sopenharmony_ci#define LDBBLOCR_CVLC_MASK (0xfff << 16) 828c2ecf20Sopenharmony_ci#define LDBBLOCR_CVLC_SHIFT 16 838c2ecf20Sopenharmony_ci#define LDBBLOCR_CHLC_MASK (0xfff << 0) 848c2ecf20Sopenharmony_ci#define LDBBLOCR_CHLC_SHIFT 0 858c2ecf20Sopenharmony_ci#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c) 868c2ecf20Sopenharmony_ci#define LDBBSMWR_BSMWA_MASK (0xffff << 16) 878c2ecf20Sopenharmony_ci#define LDBBSMWR_BSMWA_SHIFT 16 888c2ecf20Sopenharmony_ci#define LDBBSMWR_BSMW_MASK (0xffff << 0) 898c2ecf20Sopenharmony_ci#define LDBBSMWR_BSMW_SHIFT 0 908c2ecf20Sopenharmony_ci#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10) 918c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1A_MASK (0xff << 24) 928c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1A_SHIFT 24 938c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1R_MASK (0xff << 16) 948c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1R_SHIFT 16 958c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1G_MASK (0xff << 8) 968c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1G_SHIFT 8 978c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1B_MASK (0xff << 0) 988c2ecf20Sopenharmony_ci#define LDBBSAYR_FG1B_SHIFT 0 998c2ecf20Sopenharmony_ci#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14) 1008c2ecf20Sopenharmony_ci#define LDBBSACR_FG2A_MASK (0xff << 24) 1018c2ecf20Sopenharmony_ci#define LDBBSACR_FG2A_SHIFT 24 1028c2ecf20Sopenharmony_ci#define LDBBSACR_FG2R_MASK (0xff << 16) 1038c2ecf20Sopenharmony_ci#define LDBBSACR_FG2R_SHIFT 16 1048c2ecf20Sopenharmony_ci#define LDBBSACR_FG2G_MASK (0xff << 8) 1058c2ecf20Sopenharmony_ci#define LDBBSACR_FG2G_SHIFT 8 1068c2ecf20Sopenharmony_ci#define LDBBSACR_FG2B_MASK (0xff << 0) 1078c2ecf20Sopenharmony_ci#define LDBBSACR_FG2B_SHIFT 0 1088c2ecf20Sopenharmony_ci#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18) 1098c2ecf20Sopenharmony_ci#define LDBBSAAR_AP_MASK (0xff << 24) 1108c2ecf20Sopenharmony_ci#define LDBBSAAR_AP_SHIFT 24 1118c2ecf20Sopenharmony_ci#define LDBBSAAR_R_MASK (0xff << 16) 1128c2ecf20Sopenharmony_ci#define LDBBSAAR_R_SHIFT 16 1138c2ecf20Sopenharmony_ci#define LDBBSAAR_GY_MASK (0xff << 8) 1148c2ecf20Sopenharmony_ci#define LDBBSAAR_GY_SHIFT 8 1158c2ecf20Sopenharmony_ci#define LDBBSAAR_B_MASK (0xff << 0) 1168c2ecf20Sopenharmony_ci#define LDBBSAAR_B_SHIFT 0 1178c2ecf20Sopenharmony_ci#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c) 1188c2ecf20Sopenharmony_ci#define LDBBPPCR_AP_MASK (0xff << 24) 1198c2ecf20Sopenharmony_ci#define LDBBPPCR_AP_SHIFT 24 1208c2ecf20Sopenharmony_ci#define LDBBPPCR_R_MASK (0xff << 16) 1218c2ecf20Sopenharmony_ci#define LDBBPPCR_R_SHIFT 16 1228c2ecf20Sopenharmony_ci#define LDBBPPCR_GY_MASK (0xff << 8) 1238c2ecf20Sopenharmony_ci#define LDBBPPCR_GY_SHIFT 8 1248c2ecf20Sopenharmony_ci#define LDBBPPCR_B_MASK (0xff << 0) 1258c2ecf20Sopenharmony_ci#define LDBBPPCR_B_SHIFT 0 1268c2ecf20Sopenharmony_ci#define LDBnBBGCL(n) (0xb10 + (n) * 0x04) 1278c2ecf20Sopenharmony_ci#define LDBBBGCL_BGA_MASK (0xff << 24) 1288c2ecf20Sopenharmony_ci#define LDBBBGCL_BGA_SHIFT 24 1298c2ecf20Sopenharmony_ci#define LDBBBGCL_BGR_MASK (0xff << 16) 1308c2ecf20Sopenharmony_ci#define LDBBBGCL_BGR_SHIFT 16 1318c2ecf20Sopenharmony_ci#define LDBBBGCL_BGG_MASK (0xff << 8) 1328c2ecf20Sopenharmony_ci#define LDBBBGCL_BGG_SHIFT 8 1338c2ecf20Sopenharmony_ci#define LDBBBGCL_BGB_MASK (0xff << 0) 1348c2ecf20Sopenharmony_ci#define LDBBBGCL_BGB_SHIFT 0 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define SIDE_B_OFFSET 0x1000 1378c2ecf20Sopenharmony_ci#define MIRROR_OFFSET 0x2000 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define MAX_XRES 1920 1408c2ecf20Sopenharmony_ci#define MAX_YRES 1080 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cienum sh_mobile_lcdc_overlay_mode { 1438c2ecf20Sopenharmony_ci LCDC_OVERLAY_BLEND, 1448c2ecf20Sopenharmony_ci LCDC_OVERLAY_ROP3, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * struct sh_mobile_lcdc_overlay - LCDC display overlay 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * @channel: LCDC channel this overlay belongs to 1518c2ecf20Sopenharmony_ci * @cfg: Overlay configuration 1528c2ecf20Sopenharmony_ci * @info: Frame buffer device 1538c2ecf20Sopenharmony_ci * @index: Overlay index (0-3) 1548c2ecf20Sopenharmony_ci * @base: Overlay registers base address 1558c2ecf20Sopenharmony_ci * @enabled: True if the overlay is enabled 1568c2ecf20Sopenharmony_ci * @mode: Overlay blending mode (alpha blend or ROP3) 1578c2ecf20Sopenharmony_ci * @alpha: Global alpha blending value (0-255, for alpha blending mode) 1588c2ecf20Sopenharmony_ci * @rop3: Raster operation (for ROP3 mode) 1598c2ecf20Sopenharmony_ci * @fb_mem: Frame buffer virtual memory address 1608c2ecf20Sopenharmony_ci * @fb_size: Frame buffer size in bytes 1618c2ecf20Sopenharmony_ci * @dma_handle: Frame buffer DMA address 1628c2ecf20Sopenharmony_ci * @base_addr_y: Overlay base address (RGB or luma component) 1638c2ecf20Sopenharmony_ci * @base_addr_c: Overlay base address (chroma component) 1648c2ecf20Sopenharmony_ci * @pan_y_offset: Panning linear offset in bytes (luma component) 1658c2ecf20Sopenharmony_ci * @format: Current pixelf format 1668c2ecf20Sopenharmony_ci * @xres: Horizontal visible resolution 1678c2ecf20Sopenharmony_ci * @xres_virtual: Horizontal total resolution 1688c2ecf20Sopenharmony_ci * @yres: Vertical visible resolution 1698c2ecf20Sopenharmony_ci * @yres_virtual: Vertical total resolution 1708c2ecf20Sopenharmony_ci * @pitch: Overlay line pitch 1718c2ecf20Sopenharmony_ci * @pos_x: Horizontal overlay position 1728c2ecf20Sopenharmony_ci * @pos_y: Vertical overlay position 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistruct sh_mobile_lcdc_overlay { 1758c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *channel; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_overlay_cfg *cfg; 1788c2ecf20Sopenharmony_ci struct fb_info *info; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci unsigned int index; 1818c2ecf20Sopenharmony_ci unsigned long base; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci bool enabled; 1848c2ecf20Sopenharmony_ci enum sh_mobile_lcdc_overlay_mode mode; 1858c2ecf20Sopenharmony_ci unsigned int alpha; 1868c2ecf20Sopenharmony_ci unsigned int rop3; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci void *fb_mem; 1898c2ecf20Sopenharmony_ci unsigned long fb_size; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 1928c2ecf20Sopenharmony_ci unsigned long base_addr_y; 1938c2ecf20Sopenharmony_ci unsigned long base_addr_c; 1948c2ecf20Sopenharmony_ci unsigned long pan_y_offset; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_format_info *format; 1978c2ecf20Sopenharmony_ci unsigned int xres; 1988c2ecf20Sopenharmony_ci unsigned int xres_virtual; 1998c2ecf20Sopenharmony_ci unsigned int yres; 2008c2ecf20Sopenharmony_ci unsigned int yres_virtual; 2018c2ecf20Sopenharmony_ci unsigned int pitch; 2028c2ecf20Sopenharmony_ci int pos_x; 2038c2ecf20Sopenharmony_ci int pos_y; 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistruct sh_mobile_lcdc_priv { 2078c2ecf20Sopenharmony_ci void __iomem *base; 2088c2ecf20Sopenharmony_ci int irq; 2098c2ecf20Sopenharmony_ci atomic_t hw_usecnt; 2108c2ecf20Sopenharmony_ci struct device *dev; 2118c2ecf20Sopenharmony_ci struct clk *dot_clk; 2128c2ecf20Sopenharmony_ci unsigned long lddckr; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan ch[2]; 2158c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay overlays[4]; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci int started; 2188c2ecf20Sopenharmony_ci int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2228c2ecf20Sopenharmony_ci * Registers access 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { 2268c2ecf20Sopenharmony_ci [LDDCKPAT1R] = 0x400, 2278c2ecf20Sopenharmony_ci [LDDCKPAT2R] = 0x404, 2288c2ecf20Sopenharmony_ci [LDMT1R] = 0x418, 2298c2ecf20Sopenharmony_ci [LDMT2R] = 0x41c, 2308c2ecf20Sopenharmony_ci [LDMT3R] = 0x420, 2318c2ecf20Sopenharmony_ci [LDDFR] = 0x424, 2328c2ecf20Sopenharmony_ci [LDSM1R] = 0x428, 2338c2ecf20Sopenharmony_ci [LDSM2R] = 0x42c, 2348c2ecf20Sopenharmony_ci [LDSA1R] = 0x430, 2358c2ecf20Sopenharmony_ci [LDSA2R] = 0x434, 2368c2ecf20Sopenharmony_ci [LDMLSR] = 0x438, 2378c2ecf20Sopenharmony_ci [LDHCNR] = 0x448, 2388c2ecf20Sopenharmony_ci [LDHSYNR] = 0x44c, 2398c2ecf20Sopenharmony_ci [LDVLNR] = 0x450, 2408c2ecf20Sopenharmony_ci [LDVSYNR] = 0x454, 2418c2ecf20Sopenharmony_ci [LDPMR] = 0x460, 2428c2ecf20Sopenharmony_ci [LDHAJR] = 0x4a0, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { 2468c2ecf20Sopenharmony_ci [LDDCKPAT1R] = 0x408, 2478c2ecf20Sopenharmony_ci [LDDCKPAT2R] = 0x40c, 2488c2ecf20Sopenharmony_ci [LDMT1R] = 0x600, 2498c2ecf20Sopenharmony_ci [LDMT2R] = 0x604, 2508c2ecf20Sopenharmony_ci [LDMT3R] = 0x608, 2518c2ecf20Sopenharmony_ci [LDDFR] = 0x60c, 2528c2ecf20Sopenharmony_ci [LDSM1R] = 0x610, 2538c2ecf20Sopenharmony_ci [LDSM2R] = 0x614, 2548c2ecf20Sopenharmony_ci [LDSA1R] = 0x618, 2558c2ecf20Sopenharmony_ci [LDMLSR] = 0x620, 2568c2ecf20Sopenharmony_ci [LDHCNR] = 0x624, 2578c2ecf20Sopenharmony_ci [LDHSYNR] = 0x628, 2588c2ecf20Sopenharmony_ci [LDVLNR] = 0x62c, 2598c2ecf20Sopenharmony_ci [LDVSYNR] = 0x630, 2608c2ecf20Sopenharmony_ci [LDPMR] = 0x63c, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic bool banked(int reg_nr) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci switch (reg_nr) { 2668c2ecf20Sopenharmony_ci case LDMT1R: 2678c2ecf20Sopenharmony_ci case LDMT2R: 2688c2ecf20Sopenharmony_ci case LDMT3R: 2698c2ecf20Sopenharmony_ci case LDDFR: 2708c2ecf20Sopenharmony_ci case LDSM1R: 2718c2ecf20Sopenharmony_ci case LDSA1R: 2728c2ecf20Sopenharmony_ci case LDSA2R: 2738c2ecf20Sopenharmony_ci case LDMLSR: 2748c2ecf20Sopenharmony_ci case LDHCNR: 2758c2ecf20Sopenharmony_ci case LDHSYNR: 2768c2ecf20Sopenharmony_ci case LDVLNR: 2778c2ecf20Sopenharmony_ci case LDVSYNR: 2788c2ecf20Sopenharmony_ci return true; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci return false; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return chan->cfg->chan == LCDC_CHAN_SUBLCD; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 2898c2ecf20Sopenharmony_ci int reg_nr, unsigned long data) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); 2928c2ecf20Sopenharmony_ci if (banked(reg_nr)) 2938c2ecf20Sopenharmony_ci iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + 2948c2ecf20Sopenharmony_ci SIDE_B_OFFSET); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan, 2988c2ecf20Sopenharmony_ci int reg_nr, unsigned long data) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + 3018c2ecf20Sopenharmony_ci MIRROR_OFFSET); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, 3058c2ecf20Sopenharmony_ci int reg_nr) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl, 3118c2ecf20Sopenharmony_ci int reg, unsigned long data) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci iowrite32(data, ovl->channel->lcdc->base + reg); 3148c2ecf20Sopenharmony_ci iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void lcdc_write(struct sh_mobile_lcdc_priv *priv, 3188c2ecf20Sopenharmony_ci unsigned long reg_offs, unsigned long data) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci iowrite32(data, priv->base + reg_offs); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv, 3248c2ecf20Sopenharmony_ci unsigned long reg_offs) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci return ioread32(priv->base + reg_offs); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, 3308c2ecf20Sopenharmony_ci unsigned long reg_offs, 3318c2ecf20Sopenharmony_ci unsigned long mask, unsigned long until) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci while ((lcdc_read(priv, reg_offs) & mask) != until) 3348c2ecf20Sopenharmony_ci cpu_relax(); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3388c2ecf20Sopenharmony_ci * Clock management 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci if (atomic_inc_and_test(&priv->hw_usecnt)) { 3448c2ecf20Sopenharmony_ci if (priv->dot_clk) 3458c2ecf20Sopenharmony_ci clk_prepare_enable(priv->dot_clk); 3468c2ecf20Sopenharmony_ci pm_runtime_get_sync(priv->dev); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { 3538c2ecf20Sopenharmony_ci pm_runtime_put(priv->dev); 3548c2ecf20Sopenharmony_ci if (priv->dot_clk) 3558c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->dot_clk); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv, 3608c2ecf20Sopenharmony_ci int clock_source) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct clk *clk; 3638c2ecf20Sopenharmony_ci char *str; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (clock_source) { 3668c2ecf20Sopenharmony_ci case LCDC_CLK_BUS: 3678c2ecf20Sopenharmony_ci str = "bus_clk"; 3688c2ecf20Sopenharmony_ci priv->lddckr = LDDCKR_ICKSEL_BUS; 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci case LCDC_CLK_PERIPHERAL: 3718c2ecf20Sopenharmony_ci str = "peripheral_clk"; 3728c2ecf20Sopenharmony_ci priv->lddckr = LDDCKR_ICKSEL_MIPI; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case LCDC_CLK_EXTERNAL: 3758c2ecf20Sopenharmony_ci str = NULL; 3768c2ecf20Sopenharmony_ci priv->lddckr = LDDCKR_ICKSEL_HDMI; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci default: 3798c2ecf20Sopenharmony_ci return -EINVAL; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (str == NULL) 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci clk = clk_get(priv->dev, str); 3868c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 3878c2ecf20Sopenharmony_ci dev_err(priv->dev, "cannot get dot clock %s\n", str); 3888c2ecf20Sopenharmony_ci return PTR_ERR(clk); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci priv->dot_clk = clk; 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3968c2ecf20Sopenharmony_ci * Display, panel and deferred I/O 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void lcdc_sys_write_index(void *handle, unsigned long data) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = handle; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT); 4048c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4058c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | 4068c2ecf20Sopenharmony_ci (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 4078c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void lcdc_sys_write_data(void *handle, unsigned long data) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = handle; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW); 4158c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4168c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | 4178c2ecf20Sopenharmony_ci (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 4188c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic unsigned long lcdc_sys_read_data(void *handle) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = handle; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR); 4268c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4278c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA | 4288c2ecf20Sopenharmony_ci (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 4298c2ecf20Sopenharmony_ci udelay(1); 4308c2ecf20Sopenharmony_ci lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { 4368c2ecf20Sopenharmony_ci .write_index = lcdc_sys_write_index, 4378c2ecf20Sopenharmony_ci .write_data = lcdc_sys_write_data, 4388c2ecf20Sopenharmony_ci .read_data = lcdc_sys_read_data, 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_sginit(struct fb_info *info, 4428c2ecf20Sopenharmony_ci struct list_head *pagelist) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 4458c2ecf20Sopenharmony_ci unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT; 4468c2ecf20Sopenharmony_ci struct page *page; 4478c2ecf20Sopenharmony_ci int nr_pages = 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci sg_init_table(ch->sglist, nr_pages_max); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci list_for_each_entry(page, pagelist, lru) 4528c2ecf20Sopenharmony_ci sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return nr_pages; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_deferred_io(struct fb_info *info, 4588c2ecf20Sopenharmony_ci struct list_head *pagelist) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 4618c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* enable clocks before accessing hardware */ 4648c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_on(ch->lcdc); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * It's possible to get here without anything on the pagelist via 4688c2ecf20Sopenharmony_ci * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() 4698c2ecf20Sopenharmony_ci * invocation. In the former case, the acceleration routines are 4708c2ecf20Sopenharmony_ci * stepped in to when using the framebuffer console causing the 4718c2ecf20Sopenharmony_ci * workqueue to be scheduled without any dirty pages on the list. 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Despite this, a panel update is still needed given that the 4748c2ecf20Sopenharmony_ci * acceleration routines have their own methods for writing in 4758c2ecf20Sopenharmony_ci * that still need to be updated. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * The fsync() and empty pagelist case could be optimized for, 4788c2ecf20Sopenharmony_ci * but we don't bother, as any application exhibiting such 4798c2ecf20Sopenharmony_ci * behaviour is fundamentally broken anyways. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci if (!list_empty(pagelist)) { 4828c2ecf20Sopenharmony_ci unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* trigger panel update */ 4858c2ecf20Sopenharmony_ci dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 4868c2ecf20Sopenharmony_ci if (panel->start_transfer) 4878c2ecf20Sopenharmony_ci panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); 4888c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); 4898c2ecf20Sopenharmony_ci dma_unmap_sg(ch->lcdc->dev, ch->sglist, nr_pages, 4908c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci if (panel->start_transfer) 4938c2ecf20Sopenharmony_ci panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops); 4948c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct fb_deferred_io *fbdefio = info->fbdefio; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (fbdefio) 5038c2ecf20Sopenharmony_ci schedule_delayed_work(&info->deferred_work, fbdefio->delay); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (ch->tx_dev) { 5118c2ecf20Sopenharmony_ci int ret; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ret = ch->tx_dev->ops->display_on(ch->tx_dev); 5148c2ecf20Sopenharmony_ci if (ret < 0) 5158c2ecf20Sopenharmony_ci return; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (ret == SH_MOBILE_LCDC_DISPLAY_DISCONNECTED) 5188c2ecf20Sopenharmony_ci ch->info->state = FBINFO_STATE_SUSPENDED; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* HDMI must be enabled before LCDC configuration */ 5228c2ecf20Sopenharmony_ci if (panel->display_on) 5238c2ecf20Sopenharmony_ci panel->display_on(); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (panel->display_off) 5318c2ecf20Sopenharmony_ci panel->display_off(); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (ch->tx_dev) 5348c2ecf20Sopenharmony_ci ch->tx_dev->ops->display_off(ch->tx_dev); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, 5388c2ecf20Sopenharmony_ci struct fb_info *info); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 5418c2ecf20Sopenharmony_ci * Format helpers 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistruct sh_mobile_lcdc_format_info { 5458c2ecf20Sopenharmony_ci u32 fourcc; 5468c2ecf20Sopenharmony_ci unsigned int bpp; 5478c2ecf20Sopenharmony_ci bool yuv; 5488c2ecf20Sopenharmony_ci u32 lddfr; 5498c2ecf20Sopenharmony_ci}; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic const struct sh_mobile_lcdc_format_info sh_mobile_format_infos[] = { 5528c2ecf20Sopenharmony_ci { 5538c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 5548c2ecf20Sopenharmony_ci .bpp = 16, 5558c2ecf20Sopenharmony_ci .yuv = false, 5568c2ecf20Sopenharmony_ci .lddfr = LDDFR_PKF_RGB16, 5578c2ecf20Sopenharmony_ci }, { 5588c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR24, 5598c2ecf20Sopenharmony_ci .bpp = 24, 5608c2ecf20Sopenharmony_ci .yuv = false, 5618c2ecf20Sopenharmony_ci .lddfr = LDDFR_PKF_RGB24, 5628c2ecf20Sopenharmony_ci }, { 5638c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR32, 5648c2ecf20Sopenharmony_ci .bpp = 32, 5658c2ecf20Sopenharmony_ci .yuv = false, 5668c2ecf20Sopenharmony_ci .lddfr = LDDFR_PKF_ARGB32, 5678c2ecf20Sopenharmony_ci }, { 5688c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV12, 5698c2ecf20Sopenharmony_ci .bpp = 12, 5708c2ecf20Sopenharmony_ci .yuv = true, 5718c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_420, 5728c2ecf20Sopenharmony_ci }, { 5738c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV21, 5748c2ecf20Sopenharmony_ci .bpp = 12, 5758c2ecf20Sopenharmony_ci .yuv = true, 5768c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_420, 5778c2ecf20Sopenharmony_ci }, { 5788c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV16, 5798c2ecf20Sopenharmony_ci .bpp = 16, 5808c2ecf20Sopenharmony_ci .yuv = true, 5818c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_422, 5828c2ecf20Sopenharmony_ci }, { 5838c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV61, 5848c2ecf20Sopenharmony_ci .bpp = 16, 5858c2ecf20Sopenharmony_ci .yuv = true, 5868c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_422, 5878c2ecf20Sopenharmony_ci }, { 5888c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV24, 5898c2ecf20Sopenharmony_ci .bpp = 24, 5908c2ecf20Sopenharmony_ci .yuv = true, 5918c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_444, 5928c2ecf20Sopenharmony_ci }, { 5938c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV42, 5948c2ecf20Sopenharmony_ci .bpp = 24, 5958c2ecf20Sopenharmony_ci .yuv = true, 5968c2ecf20Sopenharmony_ci .lddfr = LDDFR_CC | LDDFR_YF_444, 5978c2ecf20Sopenharmony_ci }, 5988c2ecf20Sopenharmony_ci}; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic const struct sh_mobile_lcdc_format_info * 6018c2ecf20Sopenharmony_cish_mobile_format_info(u32 fourcc) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci unsigned int i; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sh_mobile_format_infos); ++i) { 6068c2ecf20Sopenharmony_ci if (sh_mobile_format_infos[i].fourcc == fourcc) 6078c2ecf20Sopenharmony_ci return &sh_mobile_format_infos[i]; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return NULL; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci if (var->grayscale > 1) 6168c2ecf20Sopenharmony_ci return var->grayscale; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 6198c2ecf20Sopenharmony_ci case 16: 6208c2ecf20Sopenharmony_ci return V4L2_PIX_FMT_RGB565; 6218c2ecf20Sopenharmony_ci case 24: 6228c2ecf20Sopenharmony_ci return V4L2_PIX_FMT_BGR24; 6238c2ecf20Sopenharmony_ci case 32: 6248c2ecf20Sopenharmony_ci return V4L2_PIX_FMT_BGR32; 6258c2ecf20Sopenharmony_ci default: 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci return var->grayscale > 1; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6368c2ecf20Sopenharmony_ci * Start, stop and IRQ 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = data; 6428c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch; 6438c2ecf20Sopenharmony_ci unsigned long ldintr; 6448c2ecf20Sopenharmony_ci int is_sub; 6458c2ecf20Sopenharmony_ci int k; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Acknowledge interrupts and disable further VSYNC End IRQs. */ 6488c2ecf20Sopenharmony_ci ldintr = lcdc_read(priv, _LDINTR); 6498c2ecf20Sopenharmony_ci lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* figure out if this interrupt is for main or sub lcd */ 6528c2ecf20Sopenharmony_ci is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* wake up channel and disable clocks */ 6558c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 6568c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!ch->enabled) 6598c2ecf20Sopenharmony_ci continue; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Frame End */ 6628c2ecf20Sopenharmony_ci if (ldintr & LDINTR_FS) { 6638c2ecf20Sopenharmony_ci if (is_sub == lcdc_chan_is_sublcd(ch)) { 6648c2ecf20Sopenharmony_ci ch->frame_end = 1; 6658c2ecf20Sopenharmony_ci wake_up(&ch->frame_end_wait); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_off(priv); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* VSYNC End */ 6728c2ecf20Sopenharmony_ci if (ldintr & LDINTR_VES) 6738c2ecf20Sopenharmony_ci complete(&ch->vsync_completion); 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci unsigned long ldintr; 6828c2ecf20Sopenharmony_ci int ret; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Enable VSync End interrupt and be careful not to acknowledge any 6858c2ecf20Sopenharmony_ci * pending interrupt. 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci ldintr = lcdc_read(ch->lcdc, _LDINTR); 6888c2ecf20Sopenharmony_ci ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; 6898c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDINTR, ldintr); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, 6928c2ecf20Sopenharmony_ci msecs_to_jiffies(100)); 6938c2ecf20Sopenharmony_ci if (!ret) 6948c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, 7008c2ecf20Sopenharmony_ci int start) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci unsigned long tmp = lcdc_read(priv, _LDCNT2R); 7038c2ecf20Sopenharmony_ci int k; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* start or stop the lcdc */ 7068c2ecf20Sopenharmony_ci if (start) 7078c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO); 7088c2ecf20Sopenharmony_ci else 7098c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* wait until power is applied/stopped on all channels */ 7128c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 7138c2ecf20Sopenharmony_ci if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) 7148c2ecf20Sopenharmony_ci while (1) { 7158c2ecf20Sopenharmony_ci tmp = lcdc_read_chan(&priv->ch[k], LDPMR) 7168c2ecf20Sopenharmony_ci & LDPMR_LPS; 7178c2ecf20Sopenharmony_ci if (start && tmp == LDPMR_LPS) 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci if (!start && tmp == 0) 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci cpu_relax(); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!start) 7258c2ecf20Sopenharmony_ci lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci const struct fb_var_screeninfo *var = &ch->info->var; 7318c2ecf20Sopenharmony_ci const struct fb_videomode *mode = &ch->display.mode; 7328c2ecf20Sopenharmony_ci unsigned long h_total, hsync_pos, display_h_total; 7338c2ecf20Sopenharmony_ci u32 tmp; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci tmp = ch->ldmt1r_value; 7368c2ecf20Sopenharmony_ci tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; 7378c2ecf20Sopenharmony_ci tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; 7388c2ecf20Sopenharmony_ci tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; 7398c2ecf20Sopenharmony_ci tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; 7408c2ecf20Sopenharmony_ci tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; 7418c2ecf20Sopenharmony_ci tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; 7428c2ecf20Sopenharmony_ci tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; 7438c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDMT1R, tmp); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* setup SYS bus */ 7468c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r); 7478c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* horizontal configuration */ 7508c2ecf20Sopenharmony_ci h_total = mode->xres + mode->hsync_len + mode->left_margin 7518c2ecf20Sopenharmony_ci + mode->right_margin; 7528c2ecf20Sopenharmony_ci tmp = h_total / 8; /* HTCN */ 7538c2ecf20Sopenharmony_ci tmp |= (min(mode->xres, ch->xres) / 8) << 16; /* HDCN */ 7548c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDHCNR, tmp); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci hsync_pos = mode->xres + mode->right_margin; 7578c2ecf20Sopenharmony_ci tmp = hsync_pos / 8; /* HSYNP */ 7588c2ecf20Sopenharmony_ci tmp |= (mode->hsync_len / 8) << 16; /* HSYNW */ 7598c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDHSYNR, tmp); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* vertical configuration */ 7628c2ecf20Sopenharmony_ci tmp = mode->yres + mode->vsync_len + mode->upper_margin 7638c2ecf20Sopenharmony_ci + mode->lower_margin; /* VTLN */ 7648c2ecf20Sopenharmony_ci tmp |= min(mode->yres, ch->yres) << 16; /* VDLN */ 7658c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDVLNR, tmp); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci tmp = mode->yres + mode->lower_margin; /* VSYNP */ 7688c2ecf20Sopenharmony_ci tmp |= mode->vsync_len << 16; /* VSYNW */ 7698c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDVSYNR, tmp); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* Adjust horizontal synchronisation for HDMI */ 7728c2ecf20Sopenharmony_ci display_h_total = mode->xres + mode->hsync_len + mode->left_margin 7738c2ecf20Sopenharmony_ci + mode->right_margin; 7748c2ecf20Sopenharmony_ci tmp = ((mode->xres & 7) << 24) | ((display_h_total & 7) << 16) 7758c2ecf20Sopenharmony_ci | ((mode->hsync_len & 7) << 8) | (hsync_pos & 7); 7768c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDHAJR, tmp); 7778c2ecf20Sopenharmony_ci lcdc_write_chan_mirror(ch, LDHAJR, tmp); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci u32 format = 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (!ovl->enabled) { 7858c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); 7868c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0); 7878c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, 7888c2ecf20Sopenharmony_ci LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); 7898c2ecf20Sopenharmony_ci return; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci ovl->base_addr_y = ovl->dma_handle; 7938c2ecf20Sopenharmony_ci ovl->base_addr_c = ovl->dma_handle 7948c2ecf20Sopenharmony_ci + ovl->xres_virtual * ovl->yres_virtual; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci switch (ovl->mode) { 7978c2ecf20Sopenharmony_ci case LCDC_OVERLAY_BLEND: 7988c2ecf20Sopenharmony_ci format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT); 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci case LCDC_OVERLAY_ROP3: 8028c2ecf20Sopenharmony_ci format = LDBBSIFR_EN | LDBBSIFR_BRSEL 8038c2ecf20Sopenharmony_ci | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT); 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci switch (ovl->format->fourcc) { 8088c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 8098c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 8108c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 8118c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV42: 8128c2ecf20Sopenharmony_ci format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; 8138c2ecf20Sopenharmony_ci break; 8148c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR24: 8158c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 8168c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 8178c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV24: 8188c2ecf20Sopenharmony_ci format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 8218c2ecf20Sopenharmony_ci default: 8228c2ecf20Sopenharmony_ci format |= LDBBSIFR_SWPL; 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci switch (ovl->format->fourcc) { 8278c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 8288c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR24: 8318c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; 8328c2ecf20Sopenharmony_ci break; 8338c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 8348c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; 8358c2ecf20Sopenharmony_ci break; 8368c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 8378c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 8388c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; 8398c2ecf20Sopenharmony_ci break; 8408c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 8418c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 8428c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV24: 8458c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV42: 8468c2ecf20Sopenharmony_ci format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index), 8558c2ecf20Sopenharmony_ci (ovl->yres << LDBBSSZR_BVSS_SHIFT) | 8568c2ecf20Sopenharmony_ci (ovl->xres << LDBBSSZR_BHSS_SHIFT)); 8578c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index), 8588c2ecf20Sopenharmony_ci (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) | 8598c2ecf20Sopenharmony_ci (ovl->pos_x << LDBBLOCR_CHLC_SHIFT)); 8608c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index), 8618c2ecf20Sopenharmony_ci ovl->pitch << LDBBSMWR_BSMW_SHIFT); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); 8648c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, 8678c2ecf20Sopenharmony_ci LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/* 8718c2ecf20Sopenharmony_ci * __sh_mobile_lcdc_start - Configure and start the LCDC 8728c2ecf20Sopenharmony_ci * @priv: LCDC device 8738c2ecf20Sopenharmony_ci * 8748c2ecf20Sopenharmony_ci * Configure all enabled channels and start the LCDC device. All external 8758c2ecf20Sopenharmony_ci * devices (clocks, MERAM, panels, ...) are not touched by this function. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_cistatic void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch; 8808c2ecf20Sopenharmony_ci unsigned long tmp; 8818c2ecf20Sopenharmony_ci int k, m; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Enable LCDC channels. Read data from external memory, avoid using the 8848c2ecf20Sopenharmony_ci * BEU for now. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* Stop the LCDC first and disable all interrupts. */ 8898c2ecf20Sopenharmony_ci sh_mobile_lcdc_start_stop(priv, 0); 8908c2ecf20Sopenharmony_ci lcdc_write(priv, _LDINTR, 0); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* Configure power supply, dot clocks and start them. */ 8938c2ecf20Sopenharmony_ci tmp = priv->lddckr; 8948c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 8958c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 8968c2ecf20Sopenharmony_ci if (!ch->enabled) 8978c2ecf20Sopenharmony_ci continue; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* Power supply */ 9008c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDPMR, 0); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci m = ch->cfg->clock_divider; 9038c2ecf20Sopenharmony_ci if (!m) 9048c2ecf20Sopenharmony_ci continue; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider 9078c2ecf20Sopenharmony_ci * denominator. 9088c2ecf20Sopenharmony_ci */ 9098c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDDCKPAT1R, 0); 9108c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (m == 1) 9138c2ecf20Sopenharmony_ci m = LDDCKR_MOSEL; 9148c2ecf20Sopenharmony_ci tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci lcdc_write(priv, _LDDCKR, tmp); 9188c2ecf20Sopenharmony_ci lcdc_write(priv, _LDDCKSTPR, 0); 9198c2ecf20Sopenharmony_ci lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* Setup geometry, format, frame buffer memory and operation mode. */ 9228c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 9238c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 9248c2ecf20Sopenharmony_ci if (!ch->enabled) 9258c2ecf20Sopenharmony_ci continue; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci sh_mobile_lcdc_geometry(ch); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci tmp = ch->format->lddfr; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (ch->format->yuv) { 9328c2ecf20Sopenharmony_ci switch (ch->colorspace) { 9338c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_REC709: 9348c2ecf20Sopenharmony_ci tmp |= LDDFR_CF1; 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci case V4L2_COLORSPACE_JPEG: 9378c2ecf20Sopenharmony_ci tmp |= LDDFR_CF0; 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDDFR, tmp); 9438c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDMLSR, ch->line_size); 9448c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); 9458c2ecf20Sopenharmony_ci if (ch->format->yuv) 9468c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* When using deferred I/O mode, configure the LCDC for one-shot 9498c2ecf20Sopenharmony_ci * operation and enable the frame end interrupt. Otherwise use 9508c2ecf20Sopenharmony_ci * continuous read mode. 9518c2ecf20Sopenharmony_ci */ 9528c2ecf20Sopenharmony_ci if (ch->ldmt1r_value & LDMT1R_IFM && 9538c2ecf20Sopenharmony_ci ch->cfg->sys_bus_cfg.deferred_io_msec) { 9548c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); 9558c2ecf20Sopenharmony_ci lcdc_write(priv, _LDINTR, LDINTR_FE); 9568c2ecf20Sopenharmony_ci } else { 9578c2ecf20Sopenharmony_ci lcdc_write_chan(ch, LDSM1R, 0); 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Word and long word swap. */ 9628c2ecf20Sopenharmony_ci switch (priv->ch[0].format->fourcc) { 9638c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 9648c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 9658c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 9668c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV42: 9678c2ecf20Sopenharmony_ci tmp = LDDDSR_LS | LDDDSR_WS; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR24: 9708c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 9718c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 9728c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV24: 9738c2ecf20Sopenharmony_ci tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_BGR32: 9768c2ecf20Sopenharmony_ci default: 9778c2ecf20Sopenharmony_ci tmp = LDDDSR_LS; 9788c2ecf20Sopenharmony_ci break; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci lcdc_write(priv, _LDDDSR, tmp); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* Enable the display output. */ 9838c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT1R, LDCNT1R_DE); 9848c2ecf20Sopenharmony_ci sh_mobile_lcdc_start_stop(priv, 1); 9858c2ecf20Sopenharmony_ci priv->started = 1; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch; 9918c2ecf20Sopenharmony_ci unsigned long tmp; 9928c2ecf20Sopenharmony_ci int ret; 9938c2ecf20Sopenharmony_ci int k; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* enable clocks before accessing the hardware */ 9968c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 9978c2ecf20Sopenharmony_ci if (priv->ch[k].enabled) 9988c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_on(priv); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* reset */ 10028c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR); 10038c2ecf20Sopenharmony_ci lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 10068c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_panel_cfg *panel; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 10098c2ecf20Sopenharmony_ci if (!ch->enabled) 10108c2ecf20Sopenharmony_ci continue; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci panel = &ch->cfg->panel_cfg; 10138c2ecf20Sopenharmony_ci if (panel->setup_sys) { 10148c2ecf20Sopenharmony_ci ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops); 10158c2ecf20Sopenharmony_ci if (ret) 10168c2ecf20Sopenharmony_ci return ret; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* Compute frame buffer base address and pitch for each channel. */ 10218c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 10228c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 10238c2ecf20Sopenharmony_ci if (!ch->enabled) 10248c2ecf20Sopenharmony_ci continue; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci ch->base_addr_y = ch->dma_handle; 10278c2ecf20Sopenharmony_ci ch->base_addr_c = ch->dma_handle 10288c2ecf20Sopenharmony_ci + ch->xres_virtual * ch->yres_virtual; 10298c2ecf20Sopenharmony_ci ch->line_size = ch->pitch; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) { 10338c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k]; 10348c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* Start the LCDC. */ 10388c2ecf20Sopenharmony_ci __sh_mobile_lcdc_start(priv); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* Setup deferred I/O, tell the board code to enable the panels, and 10418c2ecf20Sopenharmony_ci * turn backlight on. 10428c2ecf20Sopenharmony_ci */ 10438c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 10448c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 10458c2ecf20Sopenharmony_ci if (!ch->enabled) 10468c2ecf20Sopenharmony_ci continue; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci tmp = ch->cfg->sys_bus_cfg.deferred_io_msec; 10498c2ecf20Sopenharmony_ci if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { 10508c2ecf20Sopenharmony_ci ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; 10518c2ecf20Sopenharmony_ci ch->defio.delay = msecs_to_jiffies(tmp); 10528c2ecf20Sopenharmony_ci ch->info->fbdefio = &ch->defio; 10538c2ecf20Sopenharmony_ci fb_deferred_io_init(ch->info); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci sh_mobile_lcdc_display_on(ch); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (ch->bl) { 10598c2ecf20Sopenharmony_ci ch->bl->props.power = FB_BLANK_UNBLANK; 10608c2ecf20Sopenharmony_ci backlight_update_status(ch->bl); 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch; 10708c2ecf20Sopenharmony_ci int k; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* clean up deferred io and ask board code to disable panel */ 10738c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 10748c2ecf20Sopenharmony_ci ch = &priv->ch[k]; 10758c2ecf20Sopenharmony_ci if (!ch->enabled) 10768c2ecf20Sopenharmony_ci continue; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* deferred io mode: 10798c2ecf20Sopenharmony_ci * flush frame, and wait for frame end interrupt 10808c2ecf20Sopenharmony_ci * clean up deferred io and enable clock 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci if (ch->info && ch->info->fbdefio) { 10838c2ecf20Sopenharmony_ci ch->frame_end = 0; 10848c2ecf20Sopenharmony_ci schedule_delayed_work(&ch->info->deferred_work, 0); 10858c2ecf20Sopenharmony_ci wait_event(ch->frame_end_wait, ch->frame_end); 10868c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(ch->info); 10878c2ecf20Sopenharmony_ci ch->info->fbdefio = NULL; 10888c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_on(priv); 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (ch->bl) { 10928c2ecf20Sopenharmony_ci ch->bl->props.power = FB_BLANK_POWERDOWN; 10938c2ecf20Sopenharmony_ci backlight_update_status(ch->bl); 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci sh_mobile_lcdc_display_off(ch); 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* stop the lcdc */ 11008c2ecf20Sopenharmony_ci if (priv->started) { 11018c2ecf20Sopenharmony_ci sh_mobile_lcdc_start_stop(priv, 0); 11028c2ecf20Sopenharmony_ci priv->started = 0; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* stop clocks */ 11068c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 11078c2ecf20Sopenharmony_ci if (priv->ch[k].enabled) 11088c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_off(priv); 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, 11128c2ecf20Sopenharmony_ci struct fb_info *info) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci if (var->xres > MAX_XRES || var->yres > MAX_YRES) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* Make sure the virtual resolution is at least as big as the visible 11188c2ecf20Sopenharmony_ci * resolution. 11198c2ecf20Sopenharmony_ci */ 11208c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xres) 11218c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 11228c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yres) 11238c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci if (sh_mobile_format_is_fourcc(var)) { 11268c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_format_info *format; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci format = sh_mobile_format_info(var->grayscale); 11298c2ecf20Sopenharmony_ci if (format == NULL) 11308c2ecf20Sopenharmony_ci return -EINVAL; 11318c2ecf20Sopenharmony_ci var->bits_per_pixel = format->bpp; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* Default to RGB and JPEG color-spaces for RGB and YUV formats 11348c2ecf20Sopenharmony_ci * respectively. 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci if (!format->yuv) 11378c2ecf20Sopenharmony_ci var->colorspace = V4L2_COLORSPACE_SRGB; 11388c2ecf20Sopenharmony_ci else if (var->colorspace != V4L2_COLORSPACE_REC709) 11398c2ecf20Sopenharmony_ci var->colorspace = V4L2_COLORSPACE_JPEG; 11408c2ecf20Sopenharmony_ci } else { 11418c2ecf20Sopenharmony_ci if (var->bits_per_pixel <= 16) { /* RGB 565 */ 11428c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 11438c2ecf20Sopenharmony_ci var->red.offset = 11; 11448c2ecf20Sopenharmony_ci var->red.length = 5; 11458c2ecf20Sopenharmony_ci var->green.offset = 5; 11468c2ecf20Sopenharmony_ci var->green.length = 6; 11478c2ecf20Sopenharmony_ci var->blue.offset = 0; 11488c2ecf20Sopenharmony_ci var->blue.length = 5; 11498c2ecf20Sopenharmony_ci var->transp.offset = 0; 11508c2ecf20Sopenharmony_ci var->transp.length = 0; 11518c2ecf20Sopenharmony_ci } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ 11528c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 11538c2ecf20Sopenharmony_ci var->red.offset = 16; 11548c2ecf20Sopenharmony_ci var->red.length = 8; 11558c2ecf20Sopenharmony_ci var->green.offset = 8; 11568c2ecf20Sopenharmony_ci var->green.length = 8; 11578c2ecf20Sopenharmony_ci var->blue.offset = 0; 11588c2ecf20Sopenharmony_ci var->blue.length = 8; 11598c2ecf20Sopenharmony_ci var->transp.offset = 0; 11608c2ecf20Sopenharmony_ci var->transp.length = 0; 11618c2ecf20Sopenharmony_ci } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ 11628c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 11638c2ecf20Sopenharmony_ci var->red.offset = 16; 11648c2ecf20Sopenharmony_ci var->red.length = 8; 11658c2ecf20Sopenharmony_ci var->green.offset = 8; 11668c2ecf20Sopenharmony_ci var->green.length = 8; 11678c2ecf20Sopenharmony_ci var->blue.offset = 0; 11688c2ecf20Sopenharmony_ci var->blue.length = 8; 11698c2ecf20Sopenharmony_ci var->transp.offset = 24; 11708c2ecf20Sopenharmony_ci var->transp.length = 8; 11718c2ecf20Sopenharmony_ci } else 11728c2ecf20Sopenharmony_ci return -EINVAL; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci var->red.msb_right = 0; 11758c2ecf20Sopenharmony_ci var->green.msb_right = 0; 11768c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 11778c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Make sure we don't exceed our allocated memory. */ 11818c2ecf20Sopenharmony_ci if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > 11828c2ecf20Sopenharmony_ci info->fix.smem_len) 11838c2ecf20Sopenharmony_ci return -EINVAL; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 11898c2ecf20Sopenharmony_ci * Frame buffer operations - Overlays 11908c2ecf20Sopenharmony_ci */ 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic ssize_t 11938c2ecf20Sopenharmony_cioverlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11968c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha); 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic ssize_t 12028c2ecf20Sopenharmony_cioverlay_alpha_store(struct device *dev, struct device_attribute *attr, 12038c2ecf20Sopenharmony_ci const char *buf, size_t count) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12068c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 12078c2ecf20Sopenharmony_ci unsigned int alpha; 12088c2ecf20Sopenharmony_ci char *endp; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci alpha = simple_strtoul(buf, &endp, 10); 12118c2ecf20Sopenharmony_ci if (isspace(*endp)) 12128c2ecf20Sopenharmony_ci endp++; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (endp - buf != count) 12158c2ecf20Sopenharmony_ci return -EINVAL; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (alpha > 255) 12188c2ecf20Sopenharmony_ci return -EINVAL; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (ovl->alpha != alpha) { 12218c2ecf20Sopenharmony_ci ovl->alpha = alpha; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled) 12248c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci return count; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic ssize_t 12318c2ecf20Sopenharmony_cioverlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12348c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode); 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic ssize_t 12408c2ecf20Sopenharmony_cioverlay_mode_store(struct device *dev, struct device_attribute *attr, 12418c2ecf20Sopenharmony_ci const char *buf, size_t count) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12448c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 12458c2ecf20Sopenharmony_ci unsigned int mode; 12468c2ecf20Sopenharmony_ci char *endp; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci mode = simple_strtoul(buf, &endp, 10); 12498c2ecf20Sopenharmony_ci if (isspace(*endp)) 12508c2ecf20Sopenharmony_ci endp++; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (endp - buf != count) 12538c2ecf20Sopenharmony_ci return -EINVAL; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3) 12568c2ecf20Sopenharmony_ci return -EINVAL; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (ovl->mode != mode) { 12598c2ecf20Sopenharmony_ci ovl->mode = mode; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (ovl->enabled) 12628c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci return count; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic ssize_t 12698c2ecf20Sopenharmony_cioverlay_position_show(struct device *dev, struct device_attribute *attr, 12708c2ecf20Sopenharmony_ci char *buf) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12738c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic ssize_t 12798c2ecf20Sopenharmony_cioverlay_position_store(struct device *dev, struct device_attribute *attr, 12808c2ecf20Sopenharmony_ci const char *buf, size_t count) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12838c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 12848c2ecf20Sopenharmony_ci char *endp; 12858c2ecf20Sopenharmony_ci int pos_x; 12868c2ecf20Sopenharmony_ci int pos_y; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci pos_x = simple_strtol(buf, &endp, 10); 12898c2ecf20Sopenharmony_ci if (*endp != ',') 12908c2ecf20Sopenharmony_ci return -EINVAL; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci pos_y = simple_strtol(endp + 1, &endp, 10); 12938c2ecf20Sopenharmony_ci if (isspace(*endp)) 12948c2ecf20Sopenharmony_ci endp++; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci if (endp - buf != count) 12978c2ecf20Sopenharmony_ci return -EINVAL; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) { 13008c2ecf20Sopenharmony_ci ovl->pos_x = pos_x; 13018c2ecf20Sopenharmony_ci ovl->pos_y = pos_y; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (ovl->enabled) 13048c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return count; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic ssize_t 13118c2ecf20Sopenharmony_cioverlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 13148c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic ssize_t 13208c2ecf20Sopenharmony_cioverlay_rop3_store(struct device *dev, struct device_attribute *attr, 13218c2ecf20Sopenharmony_ci const char *buf, size_t count) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 13248c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 13258c2ecf20Sopenharmony_ci unsigned int rop3; 13268c2ecf20Sopenharmony_ci char *endp; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci rop3 = simple_strtoul(buf, &endp, 10); 13298c2ecf20Sopenharmony_ci if (isspace(*endp)) 13308c2ecf20Sopenharmony_ci endp++; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (endp - buf != count) 13338c2ecf20Sopenharmony_ci return -EINVAL; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (rop3 > 255) 13368c2ecf20Sopenharmony_ci return -EINVAL; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (ovl->rop3 != rop3) { 13398c2ecf20Sopenharmony_ci ovl->rop3 = rop3; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled) 13428c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci return count; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic const struct device_attribute overlay_sysfs_attrs[] = { 13498c2ecf20Sopenharmony_ci __ATTR(ovl_alpha, S_IRUGO|S_IWUSR, 13508c2ecf20Sopenharmony_ci overlay_alpha_show, overlay_alpha_store), 13518c2ecf20Sopenharmony_ci __ATTR(ovl_mode, S_IRUGO|S_IWUSR, 13528c2ecf20Sopenharmony_ci overlay_mode_show, overlay_mode_store), 13538c2ecf20Sopenharmony_ci __ATTR(ovl_position, S_IRUGO|S_IWUSR, 13548c2ecf20Sopenharmony_ci overlay_position_show, overlay_position_store), 13558c2ecf20Sopenharmony_ci __ATTR(ovl_rop3, S_IRUGO|S_IWUSR, 13568c2ecf20Sopenharmony_ci overlay_rop3_show, overlay_rop3_store), 13578c2ecf20Sopenharmony_ci}; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = { 13608c2ecf20Sopenharmony_ci .id = "SH Mobile LCDC", 13618c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 13628c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 13638c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 13648c2ecf20Sopenharmony_ci .xpanstep = 1, 13658c2ecf20Sopenharmony_ci .ypanstep = 1, 13668c2ecf20Sopenharmony_ci .ywrapstep = 0, 13678c2ecf20Sopenharmony_ci .capabilities = FB_CAP_FOURCC, 13688c2ecf20Sopenharmony_ci}; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var, 13718c2ecf20Sopenharmony_ci struct fb_info *info) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 13748c2ecf20Sopenharmony_ci unsigned long base_addr_y; 13758c2ecf20Sopenharmony_ci unsigned long base_addr_c; 13768c2ecf20Sopenharmony_ci unsigned long y_offset; 13778c2ecf20Sopenharmony_ci unsigned long c_offset; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (!ovl->format->yuv) { 13808c2ecf20Sopenharmony_ci y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset) 13818c2ecf20Sopenharmony_ci * ovl->format->bpp / 8; 13828c2ecf20Sopenharmony_ci c_offset = 0; 13838c2ecf20Sopenharmony_ci } else { 13848c2ecf20Sopenharmony_ci unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1; 13858c2ecf20Sopenharmony_ci unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci y_offset = var->yoffset * ovl->xres_virtual + var->xoffset; 13888c2ecf20Sopenharmony_ci c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub 13898c2ecf20Sopenharmony_ci + var->xoffset * 2 / xsub; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* If the Y offset hasn't changed, the C offset hasn't either. There's 13938c2ecf20Sopenharmony_ci * nothing to do in that case. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci if (y_offset == ovl->pan_y_offset) 13968c2ecf20Sopenharmony_ci return 0; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* Set the source address for the next refresh */ 13998c2ecf20Sopenharmony_ci base_addr_y = ovl->dma_handle + y_offset; 14008c2ecf20Sopenharmony_ci base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual 14018c2ecf20Sopenharmony_ci + c_offset; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci ovl->base_addr_y = base_addr_y; 14048c2ecf20Sopenharmony_ci ovl->base_addr_c = base_addr_c; 14058c2ecf20Sopenharmony_ci ovl->pan_y_offset = y_offset; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index)); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y); 14108c2ecf20Sopenharmony_ci lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci lcdc_write(ovl->channel->lcdc, LDBCR, 14138c2ecf20Sopenharmony_ci LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index)); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd, 14198c2ecf20Sopenharmony_ci unsigned long arg) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci switch (cmd) { 14248c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 14258c2ecf20Sopenharmony_ci return sh_mobile_lcdc_wait_for_vsync(ovl->channel); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci default: 14288c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var, 14338c2ecf20Sopenharmony_ci struct fb_info *info) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci return __sh_mobile_lcdc_check_var(var, info); 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_overlay_set_par(struct fb_info *info) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci ovl->format = 14438c2ecf20Sopenharmony_ci sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci ovl->xres = info->var.xres; 14468c2ecf20Sopenharmony_ci ovl->xres_virtual = info->var.xres_virtual; 14478c2ecf20Sopenharmony_ci ovl->yres = info->var.yres; 14488c2ecf20Sopenharmony_ci ovl->yres_virtual = info->var.yres_virtual; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (ovl->format->yuv) 14518c2ecf20Sopenharmony_ci ovl->pitch = info->var.xres_virtual; 14528c2ecf20Sopenharmony_ci else 14538c2ecf20Sopenharmony_ci ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci info->fix.line_length = ovl->pitch; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci if (sh_mobile_format_is_fourcc(&info->var)) { 14608c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_FOURCC; 14618c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_FOURCC; 14628c2ecf20Sopenharmony_ci } else { 14638c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 14648c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci/* Overlay blanking. Disable the overlay when blanked. */ 14718c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci ovl->enabled = !blank; 14768c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_setup(ovl); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* Prevent the backlight from receiving a blanking event by returning 14798c2ecf20Sopenharmony_ci * a non-zero value. 14808c2ecf20Sopenharmony_ci */ 14818c2ecf20Sopenharmony_ci return 1; 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_cistatic int 14858c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_mmap(struct fb_info *info, struct vm_area_struct *vma) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = info->par; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return dma_mmap_coherent(ovl->channel->lcdc->dev, vma, ovl->fb_mem, 14908c2ecf20Sopenharmony_ci ovl->dma_handle, ovl->fb_size); 14918c2ecf20Sopenharmony_ci} 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic const struct fb_ops sh_mobile_lcdc_overlay_ops = { 14948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14958c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 14968c2ecf20Sopenharmony_ci .fb_write = fb_sys_write, 14978c2ecf20Sopenharmony_ci .fb_fillrect = sys_fillrect, 14988c2ecf20Sopenharmony_ci .fb_copyarea = sys_copyarea, 14998c2ecf20Sopenharmony_ci .fb_imageblit = sys_imageblit, 15008c2ecf20Sopenharmony_ci .fb_blank = sh_mobile_lcdc_overlay_blank, 15018c2ecf20Sopenharmony_ci .fb_pan_display = sh_mobile_lcdc_overlay_pan, 15028c2ecf20Sopenharmony_ci .fb_ioctl = sh_mobile_lcdc_overlay_ioctl, 15038c2ecf20Sopenharmony_ci .fb_check_var = sh_mobile_lcdc_overlay_check_var, 15048c2ecf20Sopenharmony_ci .fb_set_par = sh_mobile_lcdc_overlay_set_par, 15058c2ecf20Sopenharmony_ci .fb_mmap = sh_mobile_lcdc_overlay_mmap, 15068c2ecf20Sopenharmony_ci}; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic void 15098c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl) 15108c2ecf20Sopenharmony_ci{ 15118c2ecf20Sopenharmony_ci struct fb_info *info = ovl->info; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci if (info == NULL || info->dev == NULL) 15148c2ecf20Sopenharmony_ci return; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci unregister_framebuffer(ovl->info); 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_cistatic int 15208c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl) 15218c2ecf20Sopenharmony_ci{ 15228c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc; 15238c2ecf20Sopenharmony_ci struct fb_info *info = ovl->info; 15248c2ecf20Sopenharmony_ci unsigned int i; 15258c2ecf20Sopenharmony_ci int ret; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (info == NULL) 15288c2ecf20Sopenharmony_ci return 0; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 15318c2ecf20Sopenharmony_ci if (ret < 0) 15328c2ecf20Sopenharmony_ci return ret; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n", 15358c2ecf20Sopenharmony_ci dev_name(lcdc->dev), ovl->index, info->var.xres, 15368c2ecf20Sopenharmony_ci info->var.yres, info->var.bits_per_pixel); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) { 15398c2ecf20Sopenharmony_ci ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]); 15408c2ecf20Sopenharmony_ci if (ret < 0) 15418c2ecf20Sopenharmony_ci return ret; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci return 0; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic void 15488c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct fb_info *info = ovl->info; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (info == NULL || info->device == NULL) 15538c2ecf20Sopenharmony_ci return; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci framebuffer_release(info); 15568c2ecf20Sopenharmony_ci} 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_cistatic int 15598c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc; 15628c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var; 15638c2ecf20Sopenharmony_ci struct fb_info *info; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* Allocate and initialize the frame buffer device. */ 15668c2ecf20Sopenharmony_ci info = framebuffer_alloc(0, priv->dev); 15678c2ecf20Sopenharmony_ci if (!info) 15688c2ecf20Sopenharmony_ci return -ENOMEM; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci ovl->info = info; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT; 15738c2ecf20Sopenharmony_ci info->fbops = &sh_mobile_lcdc_overlay_ops; 15748c2ecf20Sopenharmony_ci info->device = priv->dev; 15758c2ecf20Sopenharmony_ci info->screen_buffer = ovl->fb_mem; 15768c2ecf20Sopenharmony_ci info->par = ovl; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci /* Initialize fixed screen information. Restrict pan to 2 lines steps 15798c2ecf20Sopenharmony_ci * for NV12 and NV21. 15808c2ecf20Sopenharmony_ci */ 15818c2ecf20Sopenharmony_ci info->fix = sh_mobile_lcdc_overlay_fix; 15828c2ecf20Sopenharmony_ci snprintf(info->fix.id, sizeof(info->fix.id), 15838c2ecf20Sopenharmony_ci "SH Mobile LCDC Overlay %u", ovl->index); 15848c2ecf20Sopenharmony_ci info->fix.smem_start = ovl->dma_handle; 15858c2ecf20Sopenharmony_ci info->fix.smem_len = ovl->fb_size; 15868c2ecf20Sopenharmony_ci info->fix.line_length = ovl->pitch; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (ovl->format->yuv) 15898c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_FOURCC; 15908c2ecf20Sopenharmony_ci else 15918c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci switch (ovl->format->fourcc) { 15948c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 15958c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 15968c2ecf20Sopenharmony_ci info->fix.ypanstep = 2; 15978c2ecf20Sopenharmony_ci fallthrough; 15988c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 15998c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 16008c2ecf20Sopenharmony_ci info->fix.xpanstep = 2; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* Initialize variable screen information. */ 16048c2ecf20Sopenharmony_ci var = &info->var; 16058c2ecf20Sopenharmony_ci memset(var, 0, sizeof(*var)); 16068c2ecf20Sopenharmony_ci var->xres = ovl->xres; 16078c2ecf20Sopenharmony_ci var->yres = ovl->yres; 16088c2ecf20Sopenharmony_ci var->xres_virtual = ovl->xres_virtual; 16098c2ecf20Sopenharmony_ci var->yres_virtual = ovl->yres_virtual; 16108c2ecf20Sopenharmony_ci var->activate = FB_ACTIVATE_NOW; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Use the legacy API by default for RGB formats, and the FOURCC API 16138c2ecf20Sopenharmony_ci * for YUV formats. 16148c2ecf20Sopenharmony_ci */ 16158c2ecf20Sopenharmony_ci if (!ovl->format->yuv) 16168c2ecf20Sopenharmony_ci var->bits_per_pixel = ovl->format->bpp; 16178c2ecf20Sopenharmony_ci else 16188c2ecf20Sopenharmony_ci var->grayscale = ovl->format->fourcc; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci return sh_mobile_lcdc_overlay_check_var(var, info); 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 16248c2ecf20Sopenharmony_ci * Frame buffer operations - main frame buffer 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_setcolreg(u_int regno, 16288c2ecf20Sopenharmony_ci u_int red, u_int green, u_int blue, 16298c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci u32 *palette = info->pseudo_palette; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci if (regno >= PALETTE_NR) 16348c2ecf20Sopenharmony_ci return -EINVAL; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci /* only FB_VISUAL_TRUECOLOR supported */ 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci red >>= 16 - info->var.red.length; 16398c2ecf20Sopenharmony_ci green >>= 16 - info->var.green.length; 16408c2ecf20Sopenharmony_ci blue >>= 16 - info->var.blue.length; 16418c2ecf20Sopenharmony_ci transp >>= 16 - info->var.transp.length; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci palette[regno] = (red << info->var.red.offset) | 16448c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 16458c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 16468c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci return 0; 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo sh_mobile_lcdc_fix = { 16528c2ecf20Sopenharmony_ci .id = "SH Mobile LCDC", 16538c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 16548c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 16558c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 16568c2ecf20Sopenharmony_ci .xpanstep = 1, 16578c2ecf20Sopenharmony_ci .ypanstep = 1, 16588c2ecf20Sopenharmony_ci .ywrapstep = 0, 16598c2ecf20Sopenharmony_ci .capabilities = FB_CAP_FOURCC, 16608c2ecf20Sopenharmony_ci}; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_fillrect(struct fb_info *info, 16638c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 16648c2ecf20Sopenharmony_ci{ 16658c2ecf20Sopenharmony_ci sys_fillrect(info, rect); 16668c2ecf20Sopenharmony_ci sh_mobile_lcdc_deferred_io_touch(info); 16678c2ecf20Sopenharmony_ci} 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_copyarea(struct fb_info *info, 16708c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci sys_copyarea(info, area); 16738c2ecf20Sopenharmony_ci sh_mobile_lcdc_deferred_io_touch(info); 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_imageblit(struct fb_info *info, 16778c2ecf20Sopenharmony_ci const struct fb_image *image) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci sys_imageblit(info, image); 16808c2ecf20Sopenharmony_ci sh_mobile_lcdc_deferred_io_touch(info); 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var, 16848c2ecf20Sopenharmony_ci struct fb_info *info) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 16878c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = ch->lcdc; 16888c2ecf20Sopenharmony_ci unsigned long ldrcntr; 16898c2ecf20Sopenharmony_ci unsigned long base_addr_y, base_addr_c; 16908c2ecf20Sopenharmony_ci unsigned long y_offset; 16918c2ecf20Sopenharmony_ci unsigned long c_offset; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (!ch->format->yuv) { 16948c2ecf20Sopenharmony_ci y_offset = (var->yoffset * ch->xres_virtual + var->xoffset) 16958c2ecf20Sopenharmony_ci * ch->format->bpp / 8; 16968c2ecf20Sopenharmony_ci c_offset = 0; 16978c2ecf20Sopenharmony_ci } else { 16988c2ecf20Sopenharmony_ci unsigned int xsub = ch->format->bpp < 24 ? 2 : 1; 16998c2ecf20Sopenharmony_ci unsigned int ysub = ch->format->bpp < 16 ? 2 : 1; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci y_offset = var->yoffset * ch->xres_virtual + var->xoffset; 17028c2ecf20Sopenharmony_ci c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub 17038c2ecf20Sopenharmony_ci + var->xoffset * 2 / xsub; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci /* If the Y offset hasn't changed, the C offset hasn't either. There's 17078c2ecf20Sopenharmony_ci * nothing to do in that case. 17088c2ecf20Sopenharmony_ci */ 17098c2ecf20Sopenharmony_ci if (y_offset == ch->pan_y_offset) 17108c2ecf20Sopenharmony_ci return 0; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci /* Set the source address for the next refresh */ 17138c2ecf20Sopenharmony_ci base_addr_y = ch->dma_handle + y_offset; 17148c2ecf20Sopenharmony_ci base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual 17158c2ecf20Sopenharmony_ci + c_offset; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci ch->base_addr_y = base_addr_y; 17188c2ecf20Sopenharmony_ci ch->base_addr_c = base_addr_c; 17198c2ecf20Sopenharmony_ci ch->pan_y_offset = y_offset; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); 17228c2ecf20Sopenharmony_ci if (ch->format->yuv) 17238c2ecf20Sopenharmony_ci lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci ldrcntr = lcdc_read(priv, _LDRCNTR); 17268c2ecf20Sopenharmony_ci if (lcdc_chan_is_sublcd(ch)) 17278c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); 17288c2ecf20Sopenharmony_ci else 17298c2ecf20Sopenharmony_ci lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci sh_mobile_lcdc_deferred_io_touch(info); 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci return 0; 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd, 17388c2ecf20Sopenharmony_ci unsigned long arg) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 17418c2ecf20Sopenharmony_ci int retval; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci switch (cmd) { 17448c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 17458c2ecf20Sopenharmony_ci retval = sh_mobile_lcdc_wait_for_vsync(ch); 17468c2ecf20Sopenharmony_ci break; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci default: 17498c2ecf20Sopenharmony_ci retval = -ENOIOCTLCMD; 17508c2ecf20Sopenharmony_ci break; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci return retval; 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic void sh_mobile_fb_reconfig(struct fb_info *info) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 17588c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 17598c2ecf20Sopenharmony_ci struct fb_videomode mode; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par)) 17628c2ecf20Sopenharmony_ci /* More framebuffer users are active */ 17638c2ecf20Sopenharmony_ci return; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci fb_var_to_videomode(&mode, &info->var); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (fb_mode_is_equal(&ch->display.mode, &mode)) 17688c2ecf20Sopenharmony_ci return; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci /* Display has been re-plugged, framebuffer is free now, reconfigure */ 17718c2ecf20Sopenharmony_ci var = info->var; 17728c2ecf20Sopenharmony_ci fb_videomode_to_var(&var, &ch->display.mode); 17738c2ecf20Sopenharmony_ci var.width = ch->display.width; 17748c2ecf20Sopenharmony_ci var.height = ch->display.height; 17758c2ecf20Sopenharmony_ci var.activate = FB_ACTIVATE_NOW; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (fb_set_var(info, &var) < 0) 17788c2ecf20Sopenharmony_ci /* Couldn't reconfigure, hopefully, can continue as before */ 17798c2ecf20Sopenharmony_ci return; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci fbcon_update_vcs(info, true); 17828c2ecf20Sopenharmony_ci} 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci/* 17858c2ecf20Sopenharmony_ci * Locking: both .fb_release() and .fb_open() are called with info->lock held if 17868c2ecf20Sopenharmony_ci * user == 1, or with console sem held, if user == 0. 17878c2ecf20Sopenharmony_ci */ 17888c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_release(struct fb_info *info, int user) 17898c2ecf20Sopenharmony_ci{ 17908c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci mutex_lock(&ch->open_lock); 17938c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci ch->use_count--; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci /* Nothing to reconfigure, when called from fbcon */ 17988c2ecf20Sopenharmony_ci if (user) { 17998c2ecf20Sopenharmony_ci console_lock(); 18008c2ecf20Sopenharmony_ci sh_mobile_fb_reconfig(info); 18018c2ecf20Sopenharmony_ci console_unlock(); 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci mutex_unlock(&ch->open_lock); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci return 0; 18078c2ecf20Sopenharmony_ci} 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_open(struct fb_info *info, int user) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci mutex_lock(&ch->open_lock); 18148c2ecf20Sopenharmony_ci ch->use_count++; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); 18178c2ecf20Sopenharmony_ci mutex_unlock(&ch->open_lock); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var, 18238c2ecf20Sopenharmony_ci struct fb_info *info) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 18268c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *p = ch->lcdc; 18278c2ecf20Sopenharmony_ci unsigned int best_dist = (unsigned int)-1; 18288c2ecf20Sopenharmony_ci unsigned int best_xres = 0; 18298c2ecf20Sopenharmony_ci unsigned int best_yres = 0; 18308c2ecf20Sopenharmony_ci unsigned int i; 18318c2ecf20Sopenharmony_ci int ret; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci /* If board code provides us with a list of available modes, make sure 18348c2ecf20Sopenharmony_ci * we use one of them. Find the mode closest to the requested one. The 18358c2ecf20Sopenharmony_ci * distance between two modes is defined as the size of the 18368c2ecf20Sopenharmony_ci * non-overlapping parts of the two rectangles. 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_ci for (i = 0; i < ch->cfg->num_modes; ++i) { 18398c2ecf20Sopenharmony_ci const struct fb_videomode *mode = &ch->cfg->lcd_modes[i]; 18408c2ecf20Sopenharmony_ci unsigned int dist; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci /* We can only round up. */ 18438c2ecf20Sopenharmony_ci if (var->xres > mode->xres || var->yres > mode->yres) 18448c2ecf20Sopenharmony_ci continue; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci dist = var->xres * var->yres + mode->xres * mode->yres 18478c2ecf20Sopenharmony_ci - 2 * min(var->xres, mode->xres) 18488c2ecf20Sopenharmony_ci * min(var->yres, mode->yres); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (dist < best_dist) { 18518c2ecf20Sopenharmony_ci best_xres = mode->xres; 18528c2ecf20Sopenharmony_ci best_yres = mode->yres; 18538c2ecf20Sopenharmony_ci best_dist = dist; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /* If no available mode can be used, return an error. */ 18588c2ecf20Sopenharmony_ci if (ch->cfg->num_modes != 0) { 18598c2ecf20Sopenharmony_ci if (best_dist == (unsigned int)-1) 18608c2ecf20Sopenharmony_ci return -EINVAL; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci var->xres = best_xres; 18638c2ecf20Sopenharmony_ci var->yres = best_yres; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci ret = __sh_mobile_lcdc_check_var(var, info); 18678c2ecf20Sopenharmony_ci if (ret < 0) 18688c2ecf20Sopenharmony_ci return ret; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci /* only accept the forced_fourcc for dual channel configurations */ 18718c2ecf20Sopenharmony_ci if (p->forced_fourcc && 18728c2ecf20Sopenharmony_ci p->forced_fourcc != sh_mobile_format_fourcc(var)) 18738c2ecf20Sopenharmony_ci return -EINVAL; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci return 0; 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_set_par(struct fb_info *info) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 18818c2ecf20Sopenharmony_ci int ret; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci sh_mobile_lcdc_stop(ch->lcdc); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci ch->format = sh_mobile_format_info(sh_mobile_format_fourcc(&info->var)); 18868c2ecf20Sopenharmony_ci ch->colorspace = info->var.colorspace; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci ch->xres = info->var.xres; 18898c2ecf20Sopenharmony_ci ch->xres_virtual = info->var.xres_virtual; 18908c2ecf20Sopenharmony_ci ch->yres = info->var.yres; 18918c2ecf20Sopenharmony_ci ch->yres_virtual = info->var.yres_virtual; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (ch->format->yuv) 18948c2ecf20Sopenharmony_ci ch->pitch = info->var.xres_virtual; 18958c2ecf20Sopenharmony_ci else 18968c2ecf20Sopenharmony_ci ch->pitch = info->var.xres_virtual * ch->format->bpp / 8; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci ret = sh_mobile_lcdc_start(ch->lcdc); 18998c2ecf20Sopenharmony_ci if (ret < 0) 19008c2ecf20Sopenharmony_ci dev_err(info->dev, "%s: unable to restart LCDC\n", __func__); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci info->fix.line_length = ch->pitch; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (sh_mobile_format_is_fourcc(&info->var)) { 19058c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_FOURCC; 19068c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_FOURCC; 19078c2ecf20Sopenharmony_ci } else { 19088c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 19098c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci return ret; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci/* 19168c2ecf20Sopenharmony_ci * Screen blanking. Behavior is as follows: 19178c2ecf20Sopenharmony_ci * FB_BLANK_UNBLANK: screen unblanked, clocks enabled 19188c2ecf20Sopenharmony_ci * FB_BLANK_NORMAL: screen blanked, clocks enabled 19198c2ecf20Sopenharmony_ci * FB_BLANK_VSYNC, 19208c2ecf20Sopenharmony_ci * FB_BLANK_HSYNC, 19218c2ecf20Sopenharmony_ci * FB_BLANK_POWEROFF: screen blanked, clocks disabled 19228c2ecf20Sopenharmony_ci */ 19238c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_blank(int blank, struct fb_info *info) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 19268c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *p = ch->lcdc; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* blank the screen? */ 19298c2ecf20Sopenharmony_ci if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) { 19308c2ecf20Sopenharmony_ci struct fb_fillrect rect = { 19318c2ecf20Sopenharmony_ci .width = ch->xres, 19328c2ecf20Sopenharmony_ci .height = ch->yres, 19338c2ecf20Sopenharmony_ci }; 19348c2ecf20Sopenharmony_ci sh_mobile_lcdc_fillrect(info, &rect); 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci /* turn clocks on? */ 19378c2ecf20Sopenharmony_ci if (blank <= FB_BLANK_NORMAL && ch->blank_status > FB_BLANK_NORMAL) { 19388c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_on(p); 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci /* turn clocks off? */ 19418c2ecf20Sopenharmony_ci if (blank > FB_BLANK_NORMAL && ch->blank_status <= FB_BLANK_NORMAL) { 19428c2ecf20Sopenharmony_ci /* make sure the screen is updated with the black fill before 19438c2ecf20Sopenharmony_ci * switching the clocks off. one vsync is not enough since 19448c2ecf20Sopenharmony_ci * blanking may occur in the middle of a refresh. deferred io 19458c2ecf20Sopenharmony_ci * mode will reenable the clocks and update the screen in time, 19468c2ecf20Sopenharmony_ci * so it does not need this. */ 19478c2ecf20Sopenharmony_ci if (!info->fbdefio) { 19488c2ecf20Sopenharmony_ci sh_mobile_lcdc_wait_for_vsync(ch); 19498c2ecf20Sopenharmony_ci sh_mobile_lcdc_wait_for_vsync(ch); 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_off(p); 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci ch->blank_status = blank; 19558c2ecf20Sopenharmony_ci return 0; 19568c2ecf20Sopenharmony_ci} 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_cistatic int 19598c2ecf20Sopenharmony_cish_mobile_lcdc_mmap(struct fb_info *info, struct vm_area_struct *vma) 19608c2ecf20Sopenharmony_ci{ 19618c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = info->par; 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci return dma_mmap_coherent(ch->lcdc->dev, vma, ch->fb_mem, 19648c2ecf20Sopenharmony_ci ch->dma_handle, ch->fb_size); 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic const struct fb_ops sh_mobile_lcdc_ops = { 19688c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 19698c2ecf20Sopenharmony_ci .fb_setcolreg = sh_mobile_lcdc_setcolreg, 19708c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 19718c2ecf20Sopenharmony_ci .fb_write = fb_sys_write, 19728c2ecf20Sopenharmony_ci .fb_fillrect = sh_mobile_lcdc_fillrect, 19738c2ecf20Sopenharmony_ci .fb_copyarea = sh_mobile_lcdc_copyarea, 19748c2ecf20Sopenharmony_ci .fb_imageblit = sh_mobile_lcdc_imageblit, 19758c2ecf20Sopenharmony_ci .fb_blank = sh_mobile_lcdc_blank, 19768c2ecf20Sopenharmony_ci .fb_pan_display = sh_mobile_lcdc_pan, 19778c2ecf20Sopenharmony_ci .fb_ioctl = sh_mobile_lcdc_ioctl, 19788c2ecf20Sopenharmony_ci .fb_open = sh_mobile_lcdc_open, 19798c2ecf20Sopenharmony_ci .fb_release = sh_mobile_lcdc_release, 19808c2ecf20Sopenharmony_ci .fb_check_var = sh_mobile_lcdc_check_var, 19818c2ecf20Sopenharmony_ci .fb_set_par = sh_mobile_lcdc_set_par, 19828c2ecf20Sopenharmony_ci .fb_mmap = sh_mobile_lcdc_mmap, 19838c2ecf20Sopenharmony_ci}; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_cistatic void 19868c2ecf20Sopenharmony_cish_mobile_lcdc_channel_fb_unregister(struct sh_mobile_lcdc_chan *ch) 19878c2ecf20Sopenharmony_ci{ 19888c2ecf20Sopenharmony_ci if (ch->info && ch->info->dev) 19898c2ecf20Sopenharmony_ci unregister_framebuffer(ch->info); 19908c2ecf20Sopenharmony_ci} 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_cistatic int 19938c2ecf20Sopenharmony_cish_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci struct fb_info *info = ch->info; 19968c2ecf20Sopenharmony_ci int ret; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci if (info->fbdefio) { 19998c2ecf20Sopenharmony_ci ch->sglist = vmalloc(sizeof(struct scatterlist) * 20008c2ecf20Sopenharmony_ci ch->fb_size >> PAGE_SHIFT); 20018c2ecf20Sopenharmony_ci if (!ch->sglist) 20028c2ecf20Sopenharmony_ci return -ENOMEM; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci info->bl_dev = ch->bl; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 20088c2ecf20Sopenharmony_ci if (ret < 0) 20098c2ecf20Sopenharmony_ci return ret; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n", 20128c2ecf20Sopenharmony_ci dev_name(ch->lcdc->dev), (ch->cfg->chan == LCDC_CHAN_MAINLCD) ? 20138c2ecf20Sopenharmony_ci "mainlcd" : "sublcd", info->var.xres, info->var.yres, 20148c2ecf20Sopenharmony_ci info->var.bits_per_pixel); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* deferred io mode: disable clock to save power */ 20178c2ecf20Sopenharmony_ci if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) 20188c2ecf20Sopenharmony_ci sh_mobile_lcdc_clk_off(ch->lcdc); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci return ret; 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic void 20248c2ecf20Sopenharmony_cish_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci struct fb_info *info = ch->info; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (!info || !info->device) 20298c2ecf20Sopenharmony_ci return; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci vfree(ch->sglist); 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 20348c2ecf20Sopenharmony_ci framebuffer_release(info); 20358c2ecf20Sopenharmony_ci} 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_cistatic int 20388c2ecf20Sopenharmony_cish_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch, 20398c2ecf20Sopenharmony_ci const struct fb_videomode *modes, 20408c2ecf20Sopenharmony_ci unsigned int num_modes) 20418c2ecf20Sopenharmony_ci{ 20428c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = ch->lcdc; 20438c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var; 20448c2ecf20Sopenharmony_ci struct fb_info *info; 20458c2ecf20Sopenharmony_ci int ret; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci /* Allocate and initialize the frame buffer device. Create the modes 20488c2ecf20Sopenharmony_ci * list and allocate the color map. 20498c2ecf20Sopenharmony_ci */ 20508c2ecf20Sopenharmony_ci info = framebuffer_alloc(0, priv->dev); 20518c2ecf20Sopenharmony_ci if (!info) 20528c2ecf20Sopenharmony_ci return -ENOMEM; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci ch->info = info; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT; 20578c2ecf20Sopenharmony_ci info->fbops = &sh_mobile_lcdc_ops; 20588c2ecf20Sopenharmony_ci info->device = priv->dev; 20598c2ecf20Sopenharmony_ci info->screen_buffer = ch->fb_mem; 20608c2ecf20Sopenharmony_ci info->pseudo_palette = &ch->pseudo_palette; 20618c2ecf20Sopenharmony_ci info->par = ch; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci fb_videomode_to_modelist(modes, num_modes, &info->modelist); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 20668c2ecf20Sopenharmony_ci if (ret < 0) { 20678c2ecf20Sopenharmony_ci dev_err(priv->dev, "unable to allocate cmap\n"); 20688c2ecf20Sopenharmony_ci return ret; 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* Initialize fixed screen information. Restrict pan to 2 lines steps 20728c2ecf20Sopenharmony_ci * for NV12 and NV21. 20738c2ecf20Sopenharmony_ci */ 20748c2ecf20Sopenharmony_ci info->fix = sh_mobile_lcdc_fix; 20758c2ecf20Sopenharmony_ci info->fix.smem_start = ch->dma_handle; 20768c2ecf20Sopenharmony_ci info->fix.smem_len = ch->fb_size; 20778c2ecf20Sopenharmony_ci info->fix.line_length = ch->pitch; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (ch->format->yuv) 20808c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_FOURCC; 20818c2ecf20Sopenharmony_ci else 20828c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci switch (ch->format->fourcc) { 20858c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 20868c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 20878c2ecf20Sopenharmony_ci info->fix.ypanstep = 2; 20888c2ecf20Sopenharmony_ci fallthrough; 20898c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 20908c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 20918c2ecf20Sopenharmony_ci info->fix.xpanstep = 2; 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* Initialize variable screen information using the first mode as 20958c2ecf20Sopenharmony_ci * default. 20968c2ecf20Sopenharmony_ci */ 20978c2ecf20Sopenharmony_ci var = &info->var; 20988c2ecf20Sopenharmony_ci fb_videomode_to_var(var, modes); 20998c2ecf20Sopenharmony_ci var->width = ch->display.width; 21008c2ecf20Sopenharmony_ci var->height = ch->display.height; 21018c2ecf20Sopenharmony_ci var->xres_virtual = ch->xres_virtual; 21028c2ecf20Sopenharmony_ci var->yres_virtual = ch->yres_virtual; 21038c2ecf20Sopenharmony_ci var->activate = FB_ACTIVATE_NOW; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci /* Use the legacy API by default for RGB formats, and the FOURCC API 21068c2ecf20Sopenharmony_ci * for YUV formats. 21078c2ecf20Sopenharmony_ci */ 21088c2ecf20Sopenharmony_ci if (!ch->format->yuv) 21098c2ecf20Sopenharmony_ci var->bits_per_pixel = ch->format->bpp; 21108c2ecf20Sopenharmony_ci else 21118c2ecf20Sopenharmony_ci var->grayscale = ch->format->fourcc; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci ret = sh_mobile_lcdc_check_var(var, info); 21148c2ecf20Sopenharmony_ci if (ret) 21158c2ecf20Sopenharmony_ci return ret; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci return 0; 21188c2ecf20Sopenharmony_ci} 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 21218c2ecf20Sopenharmony_ci * Backlight 21228c2ecf20Sopenharmony_ci */ 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) 21258c2ecf20Sopenharmony_ci{ 21268c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); 21278c2ecf20Sopenharmony_ci int brightness = bdev->props.brightness; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci if (bdev->props.power != FB_BLANK_UNBLANK || 21308c2ecf20Sopenharmony_ci bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) 21318c2ecf20Sopenharmony_ci brightness = 0; 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci ch->bl_brightness = brightness; 21348c2ecf20Sopenharmony_ci return ch->cfg->bl_info.set_brightness(brightness); 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci return ch->bl_brightness; 21428c2ecf20Sopenharmony_ci} 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, 21458c2ecf20Sopenharmony_ci struct fb_info *info) 21468c2ecf20Sopenharmony_ci{ 21478c2ecf20Sopenharmony_ci return (info->bl_dev == bdev); 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_cistatic const struct backlight_ops sh_mobile_lcdc_bl_ops = { 21518c2ecf20Sopenharmony_ci .options = BL_CORE_SUSPENDRESUME, 21528c2ecf20Sopenharmony_ci .update_status = sh_mobile_lcdc_update_bl, 21538c2ecf20Sopenharmony_ci .get_brightness = sh_mobile_lcdc_get_brightness, 21548c2ecf20Sopenharmony_ci .check_fb = sh_mobile_lcdc_check_fb, 21558c2ecf20Sopenharmony_ci}; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cistatic struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, 21588c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch) 21598c2ecf20Sopenharmony_ci{ 21608c2ecf20Sopenharmony_ci struct backlight_device *bl; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch, 21638c2ecf20Sopenharmony_ci &sh_mobile_lcdc_bl_ops, NULL); 21648c2ecf20Sopenharmony_ci if (IS_ERR(bl)) { 21658c2ecf20Sopenharmony_ci dev_err(parent, "unable to register backlight device: %ld\n", 21668c2ecf20Sopenharmony_ci PTR_ERR(bl)); 21678c2ecf20Sopenharmony_ci return NULL; 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci bl->props.max_brightness = ch->cfg->bl_info.max_brightness; 21718c2ecf20Sopenharmony_ci bl->props.brightness = bl->props.max_brightness; 21728c2ecf20Sopenharmony_ci backlight_update_status(bl); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci return bl; 21758c2ecf20Sopenharmony_ci} 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_cistatic void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev) 21788c2ecf20Sopenharmony_ci{ 21798c2ecf20Sopenharmony_ci backlight_device_unregister(bdev); 21808c2ecf20Sopenharmony_ci} 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 21838c2ecf20Sopenharmony_ci * Power management 21848c2ecf20Sopenharmony_ci */ 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_suspend(struct device *dev) 21878c2ecf20Sopenharmony_ci{ 21888c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci sh_mobile_lcdc_stop(platform_get_drvdata(pdev)); 21918c2ecf20Sopenharmony_ci return 0; 21928c2ecf20Sopenharmony_ci} 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_resume(struct device *dev) 21958c2ecf20Sopenharmony_ci{ 21968c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); 21998c2ecf20Sopenharmony_ci} 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_runtime_suspend(struct device *dev) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = dev_get_drvdata(dev); 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci /* turn off LCDC hardware */ 22068c2ecf20Sopenharmony_ci lcdc_write(priv, _LDCNT1R, 0); 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci return 0; 22098c2ecf20Sopenharmony_ci} 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_runtime_resume(struct device *dev) 22128c2ecf20Sopenharmony_ci{ 22138c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = dev_get_drvdata(dev); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci __sh_mobile_lcdc_start(priv); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci return 0; 22188c2ecf20Sopenharmony_ci} 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { 22218c2ecf20Sopenharmony_ci .suspend = sh_mobile_lcdc_suspend, 22228c2ecf20Sopenharmony_ci .resume = sh_mobile_lcdc_resume, 22238c2ecf20Sopenharmony_ci .runtime_suspend = sh_mobile_lcdc_runtime_suspend, 22248c2ecf20Sopenharmony_ci .runtime_resume = sh_mobile_lcdc_runtime_resume, 22258c2ecf20Sopenharmony_ci}; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 22288c2ecf20Sopenharmony_ci * Framebuffer notifier 22298c2ecf20Sopenharmony_ci */ 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 22328c2ecf20Sopenharmony_ci * Probe/remove and driver init/exit 22338c2ecf20Sopenharmony_ci */ 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_cistatic const struct fb_videomode default_720p = { 22368c2ecf20Sopenharmony_ci .name = "HDMI 720p", 22378c2ecf20Sopenharmony_ci .xres = 1280, 22388c2ecf20Sopenharmony_ci .yres = 720, 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci .left_margin = 220, 22418c2ecf20Sopenharmony_ci .right_margin = 110, 22428c2ecf20Sopenharmony_ci .hsync_len = 40, 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci .upper_margin = 20, 22458c2ecf20Sopenharmony_ci .lower_margin = 5, 22468c2ecf20Sopenharmony_ci .vsync_len = 5, 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci .pixclock = 13468, 22498c2ecf20Sopenharmony_ci .refresh = 60, 22508c2ecf20Sopenharmony_ci .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, 22518c2ecf20Sopenharmony_ci}; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_remove(struct platform_device *pdev) 22548c2ecf20Sopenharmony_ci{ 22558c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); 22568c2ecf20Sopenharmony_ci unsigned int i; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) 22598c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]); 22608c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 22618c2ecf20Sopenharmony_ci sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]); 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci sh_mobile_lcdc_stop(priv); 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) { 22668c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci sh_mobile_lcdc_overlay_fb_cleanup(ovl); 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci if (ovl->fb_mem) 22718c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, ovl->fb_size, 22728c2ecf20Sopenharmony_ci ovl->fb_mem, ovl->dma_handle); 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 22768c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci if (ch->tx_dev) { 22798c2ecf20Sopenharmony_ci ch->tx_dev->lcdc = NULL; 22808c2ecf20Sopenharmony_ci module_put(ch->cfg->tx_dev->dev.driver->owner); 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci sh_mobile_lcdc_channel_fb_cleanup(ch); 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if (ch->fb_mem) 22868c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, ch->fb_size, 22878c2ecf20Sopenharmony_ci ch->fb_mem, ch->dma_handle); 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 22918c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci if (ch->bl) 22948c2ecf20Sopenharmony_ci sh_mobile_lcdc_bl_remove(ch->bl); 22958c2ecf20Sopenharmony_ci mutex_destroy(&ch->open_lock); 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci if (priv->dot_clk) { 22998c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 23008c2ecf20Sopenharmony_ci clk_put(priv->dot_clk); 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci if (priv->base) 23048c2ecf20Sopenharmony_ci iounmap(priv->base); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci if (priv->irq) 23078c2ecf20Sopenharmony_ci free_irq(priv->irq, priv); 23088c2ecf20Sopenharmony_ci kfree(priv); 23098c2ecf20Sopenharmony_ci return 0; 23108c2ecf20Sopenharmony_ci} 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) 23138c2ecf20Sopenharmony_ci{ 23148c2ecf20Sopenharmony_ci int interface_type = ch->cfg->interface_type; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci switch (interface_type) { 23178c2ecf20Sopenharmony_ci case RGB8: 23188c2ecf20Sopenharmony_ci case RGB9: 23198c2ecf20Sopenharmony_ci case RGB12A: 23208c2ecf20Sopenharmony_ci case RGB12B: 23218c2ecf20Sopenharmony_ci case RGB16: 23228c2ecf20Sopenharmony_ci case RGB18: 23238c2ecf20Sopenharmony_ci case RGB24: 23248c2ecf20Sopenharmony_ci case SYS8A: 23258c2ecf20Sopenharmony_ci case SYS8B: 23268c2ecf20Sopenharmony_ci case SYS8C: 23278c2ecf20Sopenharmony_ci case SYS8D: 23288c2ecf20Sopenharmony_ci case SYS9: 23298c2ecf20Sopenharmony_ci case SYS12: 23308c2ecf20Sopenharmony_ci case SYS16A: 23318c2ecf20Sopenharmony_ci case SYS16B: 23328c2ecf20Sopenharmony_ci case SYS16C: 23338c2ecf20Sopenharmony_ci case SYS18: 23348c2ecf20Sopenharmony_ci case SYS24: 23358c2ecf20Sopenharmony_ci break; 23368c2ecf20Sopenharmony_ci default: 23378c2ecf20Sopenharmony_ci return -EINVAL; 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci /* SUBLCD only supports SYS interface */ 23418c2ecf20Sopenharmony_ci if (lcdc_chan_is_sublcd(ch)) { 23428c2ecf20Sopenharmony_ci if (!(interface_type & LDMT1R_IFM)) 23438c2ecf20Sopenharmony_ci return -EINVAL; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci interface_type &= ~LDMT1R_IFM; 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci ch->ldmt1r_value = interface_type; 23498c2ecf20Sopenharmony_ci return 0; 23508c2ecf20Sopenharmony_ci} 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_cistatic int 23538c2ecf20Sopenharmony_cish_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_overlay *ovl) 23548c2ecf20Sopenharmony_ci{ 23558c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_format_info *format; 23568c2ecf20Sopenharmony_ci struct device *dev = ovl->channel->lcdc->dev; 23578c2ecf20Sopenharmony_ci int ret; 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci if (ovl->cfg->fourcc == 0) 23608c2ecf20Sopenharmony_ci return 0; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci /* Validate the format. */ 23638c2ecf20Sopenharmony_ci format = sh_mobile_format_info(ovl->cfg->fourcc); 23648c2ecf20Sopenharmony_ci if (format == NULL) { 23658c2ecf20Sopenharmony_ci dev_err(dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc); 23668c2ecf20Sopenharmony_ci return -EINVAL; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci ovl->enabled = false; 23708c2ecf20Sopenharmony_ci ovl->mode = LCDC_OVERLAY_BLEND; 23718c2ecf20Sopenharmony_ci ovl->alpha = 255; 23728c2ecf20Sopenharmony_ci ovl->rop3 = 0; 23738c2ecf20Sopenharmony_ci ovl->pos_x = 0; 23748c2ecf20Sopenharmony_ci ovl->pos_y = 0; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci /* The default Y virtual resolution is twice the panel size to allow for 23778c2ecf20Sopenharmony_ci * double-buffering. 23788c2ecf20Sopenharmony_ci */ 23798c2ecf20Sopenharmony_ci ovl->format = format; 23808c2ecf20Sopenharmony_ci ovl->xres = ovl->cfg->max_xres; 23818c2ecf20Sopenharmony_ci ovl->xres_virtual = ovl->xres; 23828c2ecf20Sopenharmony_ci ovl->yres = ovl->cfg->max_yres; 23838c2ecf20Sopenharmony_ci ovl->yres_virtual = ovl->yres * 2; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci if (!format->yuv) 23868c2ecf20Sopenharmony_ci ovl->pitch = ovl->xres_virtual * format->bpp / 8; 23878c2ecf20Sopenharmony_ci else 23888c2ecf20Sopenharmony_ci ovl->pitch = ovl->xres_virtual; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci /* Allocate frame buffer memory. */ 23918c2ecf20Sopenharmony_ci ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres 23928c2ecf20Sopenharmony_ci * format->bpp / 8 * 2; 23938c2ecf20Sopenharmony_ci ovl->fb_mem = dma_alloc_coherent(dev, ovl->fb_size, &ovl->dma_handle, 23948c2ecf20Sopenharmony_ci GFP_KERNEL); 23958c2ecf20Sopenharmony_ci if (!ovl->fb_mem) { 23968c2ecf20Sopenharmony_ci dev_err(dev, "unable to allocate buffer\n"); 23978c2ecf20Sopenharmony_ci return -ENOMEM; 23988c2ecf20Sopenharmony_ci } 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci ret = sh_mobile_lcdc_overlay_fb_init(ovl); 24018c2ecf20Sopenharmony_ci if (ret < 0) 24028c2ecf20Sopenharmony_ci return ret; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci return 0; 24058c2ecf20Sopenharmony_ci} 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_cistatic int 24088c2ecf20Sopenharmony_cish_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch) 24098c2ecf20Sopenharmony_ci{ 24108c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_format_info *format; 24118c2ecf20Sopenharmony_ci const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg; 24128c2ecf20Sopenharmony_ci struct device *dev = ch->lcdc->dev; 24138c2ecf20Sopenharmony_ci const struct fb_videomode *max_mode; 24148c2ecf20Sopenharmony_ci const struct fb_videomode *mode; 24158c2ecf20Sopenharmony_ci unsigned int num_modes; 24168c2ecf20Sopenharmony_ci unsigned int max_size; 24178c2ecf20Sopenharmony_ci unsigned int i; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci /* Validate the format. */ 24208c2ecf20Sopenharmony_ci format = sh_mobile_format_info(cfg->fourcc); 24218c2ecf20Sopenharmony_ci if (format == NULL) { 24228c2ecf20Sopenharmony_ci dev_err(dev, "Invalid FOURCC %08x.\n", cfg->fourcc); 24238c2ecf20Sopenharmony_ci return -EINVAL; 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci /* Iterate through the modes to validate them and find the highest 24278c2ecf20Sopenharmony_ci * resolution. 24288c2ecf20Sopenharmony_ci */ 24298c2ecf20Sopenharmony_ci max_mode = NULL; 24308c2ecf20Sopenharmony_ci max_size = 0; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci for (i = 0, mode = cfg->lcd_modes; i < cfg->num_modes; i++, mode++) { 24338c2ecf20Sopenharmony_ci unsigned int size = mode->yres * mode->xres; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci /* NV12/NV21 buffers must have even number of lines */ 24368c2ecf20Sopenharmony_ci if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || 24378c2ecf20Sopenharmony_ci cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { 24388c2ecf20Sopenharmony_ci dev_err(dev, "yres must be multiple of 2 for " 24398c2ecf20Sopenharmony_ci "YCbCr420 mode.\n"); 24408c2ecf20Sopenharmony_ci return -EINVAL; 24418c2ecf20Sopenharmony_ci } 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (size > max_size) { 24448c2ecf20Sopenharmony_ci max_mode = mode; 24458c2ecf20Sopenharmony_ci max_size = size; 24468c2ecf20Sopenharmony_ci } 24478c2ecf20Sopenharmony_ci } 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci if (!max_size) 24508c2ecf20Sopenharmony_ci max_size = MAX_XRES * MAX_YRES; 24518c2ecf20Sopenharmony_ci else 24528c2ecf20Sopenharmony_ci dev_dbg(dev, "Found largest videomode %ux%u\n", 24538c2ecf20Sopenharmony_ci max_mode->xres, max_mode->yres); 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (cfg->lcd_modes == NULL) { 24568c2ecf20Sopenharmony_ci mode = &default_720p; 24578c2ecf20Sopenharmony_ci num_modes = 1; 24588c2ecf20Sopenharmony_ci } else { 24598c2ecf20Sopenharmony_ci mode = cfg->lcd_modes; 24608c2ecf20Sopenharmony_ci num_modes = cfg->num_modes; 24618c2ecf20Sopenharmony_ci } 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci /* Use the first mode as default. The default Y virtual resolution is 24648c2ecf20Sopenharmony_ci * twice the panel size to allow for double-buffering. 24658c2ecf20Sopenharmony_ci */ 24668c2ecf20Sopenharmony_ci ch->format = format; 24678c2ecf20Sopenharmony_ci ch->xres = mode->xres; 24688c2ecf20Sopenharmony_ci ch->xres_virtual = mode->xres; 24698c2ecf20Sopenharmony_ci ch->yres = mode->yres; 24708c2ecf20Sopenharmony_ci ch->yres_virtual = mode->yres * 2; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci if (!format->yuv) { 24738c2ecf20Sopenharmony_ci ch->colorspace = V4L2_COLORSPACE_SRGB; 24748c2ecf20Sopenharmony_ci ch->pitch = ch->xres_virtual * format->bpp / 8; 24758c2ecf20Sopenharmony_ci } else { 24768c2ecf20Sopenharmony_ci ch->colorspace = V4L2_COLORSPACE_REC709; 24778c2ecf20Sopenharmony_ci ch->pitch = ch->xres_virtual; 24788c2ecf20Sopenharmony_ci } 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci ch->display.width = cfg->panel_cfg.width; 24818c2ecf20Sopenharmony_ci ch->display.height = cfg->panel_cfg.height; 24828c2ecf20Sopenharmony_ci ch->display.mode = *mode; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci /* Allocate frame buffer memory. */ 24858c2ecf20Sopenharmony_ci ch->fb_size = max_size * format->bpp / 8 * 2; 24868c2ecf20Sopenharmony_ci ch->fb_mem = dma_alloc_coherent(dev, ch->fb_size, &ch->dma_handle, 24878c2ecf20Sopenharmony_ci GFP_KERNEL); 24888c2ecf20Sopenharmony_ci if (ch->fb_mem == NULL) { 24898c2ecf20Sopenharmony_ci dev_err(dev, "unable to allocate buffer\n"); 24908c2ecf20Sopenharmony_ci return -ENOMEM; 24918c2ecf20Sopenharmony_ci } 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci /* Initialize the transmitter device if present. */ 24948c2ecf20Sopenharmony_ci if (cfg->tx_dev) { 24958c2ecf20Sopenharmony_ci if (!cfg->tx_dev->dev.driver || 24968c2ecf20Sopenharmony_ci !try_module_get(cfg->tx_dev->dev.driver->owner)) { 24978c2ecf20Sopenharmony_ci dev_warn(dev, "unable to get transmitter device\n"); 24988c2ecf20Sopenharmony_ci return -EINVAL; 24998c2ecf20Sopenharmony_ci } 25008c2ecf20Sopenharmony_ci ch->tx_dev = platform_get_drvdata(cfg->tx_dev); 25018c2ecf20Sopenharmony_ci ch->tx_dev->lcdc = ch; 25028c2ecf20Sopenharmony_ci ch->tx_dev->def_mode = *mode; 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci return sh_mobile_lcdc_channel_fb_init(ch, mode, num_modes); 25068c2ecf20Sopenharmony_ci} 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_cistatic int sh_mobile_lcdc_probe(struct platform_device *pdev) 25098c2ecf20Sopenharmony_ci{ 25108c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data; 25118c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_priv *priv; 25128c2ecf20Sopenharmony_ci struct resource *res; 25138c2ecf20Sopenharmony_ci int num_channels; 25148c2ecf20Sopenharmony_ci int error; 25158c2ecf20Sopenharmony_ci int irq, i; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (!pdata) { 25188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data defined\n"); 25198c2ecf20Sopenharmony_ci return -EINVAL; 25208c2ecf20Sopenharmony_ci } 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 25238c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 25248c2ecf20Sopenharmony_ci if (!res || irq < 0) { 25258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot get platform resources\n"); 25268c2ecf20Sopenharmony_ci return -ENOENT; 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 25308c2ecf20Sopenharmony_ci if (!priv) 25318c2ecf20Sopenharmony_ci return -ENOMEM; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci priv->dev = &pdev->dev; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 25368c2ecf20Sopenharmony_ci mutex_init(&priv->ch[i].open_lock); 25378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci error = request_irq(irq, sh_mobile_lcdc_irq, 0, 25408c2ecf20Sopenharmony_ci dev_name(&pdev->dev), priv); 25418c2ecf20Sopenharmony_ci if (error) { 25428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to request irq\n"); 25438c2ecf20Sopenharmony_ci goto err1; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci priv->irq = irq; 25478c2ecf20Sopenharmony_ci atomic_set(&priv->hw_usecnt, -1); 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci for (i = 0, num_channels = 0; i < ARRAY_SIZE(pdata->ch); i++) { 25508c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci ch->lcdc = priv; 25538c2ecf20Sopenharmony_ci ch->cfg = &pdata->ch[i]; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_check_interface(ch); 25568c2ecf20Sopenharmony_ci if (error) { 25578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unsupported interface type\n"); 25588c2ecf20Sopenharmony_ci goto err1; 25598c2ecf20Sopenharmony_ci } 25608c2ecf20Sopenharmony_ci init_waitqueue_head(&ch->frame_end_wait); 25618c2ecf20Sopenharmony_ci init_completion(&ch->vsync_completion); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci /* probe the backlight is there is one defined */ 25648c2ecf20Sopenharmony_ci if (ch->cfg->bl_info.max_brightness) 25658c2ecf20Sopenharmony_ci ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci switch (pdata->ch[i].chan) { 25688c2ecf20Sopenharmony_ci case LCDC_CHAN_MAINLCD: 25698c2ecf20Sopenharmony_ci ch->enabled = LDCNT2R_ME; 25708c2ecf20Sopenharmony_ci ch->reg_offs = lcdc_offs_mainlcd; 25718c2ecf20Sopenharmony_ci num_channels++; 25728c2ecf20Sopenharmony_ci break; 25738c2ecf20Sopenharmony_ci case LCDC_CHAN_SUBLCD: 25748c2ecf20Sopenharmony_ci ch->enabled = LDCNT2R_SE; 25758c2ecf20Sopenharmony_ci ch->reg_offs = lcdc_offs_sublcd; 25768c2ecf20Sopenharmony_ci num_channels++; 25778c2ecf20Sopenharmony_ci break; 25788c2ecf20Sopenharmony_ci } 25798c2ecf20Sopenharmony_ci } 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci if (!num_channels) { 25828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no channels defined\n"); 25838c2ecf20Sopenharmony_ci error = -EINVAL; 25848c2ecf20Sopenharmony_ci goto err1; 25858c2ecf20Sopenharmony_ci } 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci /* for dual channel LCDC (MAIN + SUB) force shared format setting */ 25888c2ecf20Sopenharmony_ci if (num_channels == 2) 25898c2ecf20Sopenharmony_ci priv->forced_fourcc = pdata->ch[0].fourcc; 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci priv->base = ioremap(res->start, resource_size(res)); 25928c2ecf20Sopenharmony_ci if (!priv->base) { 25938c2ecf20Sopenharmony_ci error = -ENOMEM; 25948c2ecf20Sopenharmony_ci goto err1; 25958c2ecf20Sopenharmony_ci } 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source); 25988c2ecf20Sopenharmony_ci if (error) { 25998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to setup clocks\n"); 26008c2ecf20Sopenharmony_ci goto err1; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci /* Enable runtime PM. */ 26048c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci for (i = 0; i < num_channels; i++) { 26078c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = &priv->ch[i]; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_channel_init(ch); 26108c2ecf20Sopenharmony_ci if (error) 26118c2ecf20Sopenharmony_ci goto err1; 26128c2ecf20Sopenharmony_ci } 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { 26158c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci ovl->cfg = &pdata->overlays[i]; 26188c2ecf20Sopenharmony_ci ovl->channel = &priv->ch[0]; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_overlay_init(ovl); 26218c2ecf20Sopenharmony_ci if (error) 26228c2ecf20Sopenharmony_ci goto err1; 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_start(priv); 26268c2ecf20Sopenharmony_ci if (error) { 26278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to start hardware\n"); 26288c2ecf20Sopenharmony_ci goto err1; 26298c2ecf20Sopenharmony_ci } 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci for (i = 0; i < num_channels; i++) { 26328c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_chan *ch = priv->ch + i; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_channel_fb_register(ch); 26358c2ecf20Sopenharmony_ci if (error) 26368c2ecf20Sopenharmony_ci goto err1; 26378c2ecf20Sopenharmony_ci } 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) { 26408c2ecf20Sopenharmony_ci struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i]; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci error = sh_mobile_lcdc_overlay_fb_register(ovl); 26438c2ecf20Sopenharmony_ci if (error) 26448c2ecf20Sopenharmony_ci goto err1; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci return 0; 26488c2ecf20Sopenharmony_cierr1: 26498c2ecf20Sopenharmony_ci sh_mobile_lcdc_remove(pdev); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci return error; 26528c2ecf20Sopenharmony_ci} 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic struct platform_driver sh_mobile_lcdc_driver = { 26558c2ecf20Sopenharmony_ci .driver = { 26568c2ecf20Sopenharmony_ci .name = "sh_mobile_lcdc_fb", 26578c2ecf20Sopenharmony_ci .pm = &sh_mobile_lcdc_dev_pm_ops, 26588c2ecf20Sopenharmony_ci }, 26598c2ecf20Sopenharmony_ci .probe = sh_mobile_lcdc_probe, 26608c2ecf20Sopenharmony_ci .remove = sh_mobile_lcdc_remove, 26618c2ecf20Sopenharmony_ci}; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_cimodule_platform_driver(sh_mobile_lcdc_driver); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver"); 26668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); 26678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2668