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