18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Freescale DIU Frame Buffer device driver
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Authors: Hongjun Chen <hong-jun.chen@freescale.com>
88c2ecf20Sopenharmony_ci *           Paul Widmer <paul.widmer@freescale.com>
98c2ecf20Sopenharmony_ci *           Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
108c2ecf20Sopenharmony_ci *           York Sun <yorksun@freescale.com>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *   Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/fb.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/clk.h>
268c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
278c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
288c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
298c2ecf20Sopenharmony_ci#include <linux/of_address.h>
308c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h>
338c2ecf20Sopenharmony_ci#include <linux/fsl-diu-fb.h>
348c2ecf20Sopenharmony_ci#include "edid.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define NUM_AOIS	5	/* 1 for plane 0, 2 for planes 1 & 2 each */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* HW cursor parameters */
398c2ecf20Sopenharmony_ci#define MAX_CURS		32
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* INT_STATUS/INT_MASK field descriptions */
428c2ecf20Sopenharmony_ci#define INT_VSYNC	0x01	/* Vsync interrupt  */
438c2ecf20Sopenharmony_ci#define INT_VSYNC_WB	0x02	/* Vsync interrupt for write back operation */
448c2ecf20Sopenharmony_ci#define INT_UNDRUN	0x04	/* Under run exception interrupt */
458c2ecf20Sopenharmony_ci#define INT_PARERR	0x08	/* Display parameters error interrupt */
468c2ecf20Sopenharmony_ci#define INT_LS_BF_VS	0x10	/* Lines before vsync. interrupt */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * List of supported video modes
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * The first entry is the default video mode.  The remain entries are in
528c2ecf20Sopenharmony_ci * order if increasing resolution and frequency.  The 320x240-60 mode is
538c2ecf20Sopenharmony_ci * the initial AOI for the second and third planes.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistatic struct fb_videomode fsl_diu_mode_db[] = {
568c2ecf20Sopenharmony_ci	{
578c2ecf20Sopenharmony_ci		.refresh	= 60,
588c2ecf20Sopenharmony_ci		.xres		= 1024,
598c2ecf20Sopenharmony_ci		.yres		= 768,
608c2ecf20Sopenharmony_ci		.pixclock	= 15385,
618c2ecf20Sopenharmony_ci		.left_margin	= 160,
628c2ecf20Sopenharmony_ci		.right_margin	= 24,
638c2ecf20Sopenharmony_ci		.upper_margin	= 29,
648c2ecf20Sopenharmony_ci		.lower_margin	= 3,
658c2ecf20Sopenharmony_ci		.hsync_len	= 136,
668c2ecf20Sopenharmony_ci		.vsync_len	= 6,
678c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
688c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	{
718c2ecf20Sopenharmony_ci		.refresh	= 60,
728c2ecf20Sopenharmony_ci		.xres		= 320,
738c2ecf20Sopenharmony_ci		.yres		= 240,
748c2ecf20Sopenharmony_ci		.pixclock	= 79440,
758c2ecf20Sopenharmony_ci		.left_margin	= 16,
768c2ecf20Sopenharmony_ci		.right_margin	= 16,
778c2ecf20Sopenharmony_ci		.upper_margin	= 16,
788c2ecf20Sopenharmony_ci		.lower_margin	= 5,
798c2ecf20Sopenharmony_ci		.hsync_len	= 48,
808c2ecf20Sopenharmony_ci		.vsync_len	= 1,
818c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
828c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{
858c2ecf20Sopenharmony_ci		.refresh        = 60,
868c2ecf20Sopenharmony_ci		.xres           = 640,
878c2ecf20Sopenharmony_ci		.yres           = 480,
888c2ecf20Sopenharmony_ci		.pixclock       = 39722,
898c2ecf20Sopenharmony_ci		.left_margin    = 48,
908c2ecf20Sopenharmony_ci		.right_margin   = 16,
918c2ecf20Sopenharmony_ci		.upper_margin   = 33,
928c2ecf20Sopenharmony_ci		.lower_margin   = 10,
938c2ecf20Sopenharmony_ci		.hsync_len      = 96,
948c2ecf20Sopenharmony_ci		.vsync_len      = 2,
958c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
968c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci	{
998c2ecf20Sopenharmony_ci		.refresh        = 72,
1008c2ecf20Sopenharmony_ci		.xres           = 640,
1018c2ecf20Sopenharmony_ci		.yres           = 480,
1028c2ecf20Sopenharmony_ci		.pixclock       = 32052,
1038c2ecf20Sopenharmony_ci		.left_margin    = 128,
1048c2ecf20Sopenharmony_ci		.right_margin   = 24,
1058c2ecf20Sopenharmony_ci		.upper_margin   = 28,
1068c2ecf20Sopenharmony_ci		.lower_margin   = 9,
1078c2ecf20Sopenharmony_ci		.hsync_len      = 40,
1088c2ecf20Sopenharmony_ci		.vsync_len      = 3,
1098c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1108c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
1118c2ecf20Sopenharmony_ci	},
1128c2ecf20Sopenharmony_ci	{
1138c2ecf20Sopenharmony_ci		.refresh        = 75,
1148c2ecf20Sopenharmony_ci		.xres           = 640,
1158c2ecf20Sopenharmony_ci		.yres           = 480,
1168c2ecf20Sopenharmony_ci		.pixclock       = 31747,
1178c2ecf20Sopenharmony_ci		.left_margin    = 120,
1188c2ecf20Sopenharmony_ci		.right_margin   = 16,
1198c2ecf20Sopenharmony_ci		.upper_margin   = 16,
1208c2ecf20Sopenharmony_ci		.lower_margin   = 1,
1218c2ecf20Sopenharmony_ci		.hsync_len      = 64,
1228c2ecf20Sopenharmony_ci		.vsync_len      = 3,
1238c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1248c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
1258c2ecf20Sopenharmony_ci	},
1268c2ecf20Sopenharmony_ci	{
1278c2ecf20Sopenharmony_ci		.refresh        = 90,
1288c2ecf20Sopenharmony_ci		.xres           = 640,
1298c2ecf20Sopenharmony_ci		.yres           = 480,
1308c2ecf20Sopenharmony_ci		.pixclock       = 25057,
1318c2ecf20Sopenharmony_ci		.left_margin    = 120,
1328c2ecf20Sopenharmony_ci		.right_margin   = 32,
1338c2ecf20Sopenharmony_ci		.upper_margin   = 14,
1348c2ecf20Sopenharmony_ci		.lower_margin   = 25,
1358c2ecf20Sopenharmony_ci		.hsync_len      = 40,
1368c2ecf20Sopenharmony_ci		.vsync_len      = 14,
1378c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1388c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci	{
1418c2ecf20Sopenharmony_ci		.refresh        = 100,
1428c2ecf20Sopenharmony_ci		.xres           = 640,
1438c2ecf20Sopenharmony_ci		.yres           = 480,
1448c2ecf20Sopenharmony_ci		.pixclock       = 22272,
1458c2ecf20Sopenharmony_ci		.left_margin    = 48,
1468c2ecf20Sopenharmony_ci		.right_margin   = 32,
1478c2ecf20Sopenharmony_ci		.upper_margin   = 17,
1488c2ecf20Sopenharmony_ci		.lower_margin   = 22,
1498c2ecf20Sopenharmony_ci		.hsync_len      = 128,
1508c2ecf20Sopenharmony_ci		.vsync_len      = 12,
1518c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1528c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{
1558c2ecf20Sopenharmony_ci		.refresh	= 60,
1568c2ecf20Sopenharmony_ci		.xres		= 800,
1578c2ecf20Sopenharmony_ci		.yres		= 480,
1588c2ecf20Sopenharmony_ci		.pixclock	= 33805,
1598c2ecf20Sopenharmony_ci		.left_margin	= 96,
1608c2ecf20Sopenharmony_ci		.right_margin	= 24,
1618c2ecf20Sopenharmony_ci		.upper_margin	= 10,
1628c2ecf20Sopenharmony_ci		.lower_margin	= 3,
1638c2ecf20Sopenharmony_ci		.hsync_len	= 72,
1648c2ecf20Sopenharmony_ci		.vsync_len	= 7,
1658c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1668c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
1678c2ecf20Sopenharmony_ci	},
1688c2ecf20Sopenharmony_ci	{
1698c2ecf20Sopenharmony_ci		.refresh        = 60,
1708c2ecf20Sopenharmony_ci		.xres           = 800,
1718c2ecf20Sopenharmony_ci		.yres           = 600,
1728c2ecf20Sopenharmony_ci		.pixclock       = 25000,
1738c2ecf20Sopenharmony_ci		.left_margin    = 88,
1748c2ecf20Sopenharmony_ci		.right_margin   = 40,
1758c2ecf20Sopenharmony_ci		.upper_margin   = 23,
1768c2ecf20Sopenharmony_ci		.lower_margin   = 1,
1778c2ecf20Sopenharmony_ci		.hsync_len      = 128,
1788c2ecf20Sopenharmony_ci		.vsync_len      = 4,
1798c2ecf20Sopenharmony_ci		.sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1808c2ecf20Sopenharmony_ci		.vmode          = FB_VMODE_NONINTERLACED
1818c2ecf20Sopenharmony_ci	},
1828c2ecf20Sopenharmony_ci	{
1838c2ecf20Sopenharmony_ci		.refresh	= 60,
1848c2ecf20Sopenharmony_ci		.xres		= 854,
1858c2ecf20Sopenharmony_ci		.yres		= 480,
1868c2ecf20Sopenharmony_ci		.pixclock	= 31518,
1878c2ecf20Sopenharmony_ci		.left_margin	= 104,
1888c2ecf20Sopenharmony_ci		.right_margin	= 16,
1898c2ecf20Sopenharmony_ci		.upper_margin	= 13,
1908c2ecf20Sopenharmony_ci		.lower_margin	= 1,
1918c2ecf20Sopenharmony_ci		.hsync_len	= 88,
1928c2ecf20Sopenharmony_ci		.vsync_len	= 3,
1938c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1948c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
1958c2ecf20Sopenharmony_ci	},
1968c2ecf20Sopenharmony_ci	{
1978c2ecf20Sopenharmony_ci		.refresh	= 70,
1988c2ecf20Sopenharmony_ci		.xres		= 1024,
1998c2ecf20Sopenharmony_ci		.yres		= 768,
2008c2ecf20Sopenharmony_ci		.pixclock	= 16886,
2018c2ecf20Sopenharmony_ci		.left_margin	= 3,
2028c2ecf20Sopenharmony_ci		.right_margin	= 3,
2038c2ecf20Sopenharmony_ci		.upper_margin	= 2,
2048c2ecf20Sopenharmony_ci		.lower_margin	= 2,
2058c2ecf20Sopenharmony_ci		.hsync_len	= 40,
2068c2ecf20Sopenharmony_ci		.vsync_len	= 18,
2078c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2088c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2098c2ecf20Sopenharmony_ci	},
2108c2ecf20Sopenharmony_ci	{
2118c2ecf20Sopenharmony_ci		.refresh	= 75,
2128c2ecf20Sopenharmony_ci		.xres		= 1024,
2138c2ecf20Sopenharmony_ci		.yres		= 768,
2148c2ecf20Sopenharmony_ci		.pixclock	= 15009,
2158c2ecf20Sopenharmony_ci		.left_margin	= 3,
2168c2ecf20Sopenharmony_ci		.right_margin	= 3,
2178c2ecf20Sopenharmony_ci		.upper_margin	= 2,
2188c2ecf20Sopenharmony_ci		.lower_margin	= 2,
2198c2ecf20Sopenharmony_ci		.hsync_len	= 80,
2208c2ecf20Sopenharmony_ci		.vsync_len	= 32,
2218c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2228c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2238c2ecf20Sopenharmony_ci	},
2248c2ecf20Sopenharmony_ci	{
2258c2ecf20Sopenharmony_ci		.refresh	= 60,
2268c2ecf20Sopenharmony_ci		.xres		= 1280,
2278c2ecf20Sopenharmony_ci		.yres		= 480,
2288c2ecf20Sopenharmony_ci		.pixclock	= 18939,
2298c2ecf20Sopenharmony_ci		.left_margin	= 353,
2308c2ecf20Sopenharmony_ci		.right_margin	= 47,
2318c2ecf20Sopenharmony_ci		.upper_margin	= 39,
2328c2ecf20Sopenharmony_ci		.lower_margin	= 4,
2338c2ecf20Sopenharmony_ci		.hsync_len	= 8,
2348c2ecf20Sopenharmony_ci		.vsync_len	= 2,
2358c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2368c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	{
2398c2ecf20Sopenharmony_ci		.refresh	= 60,
2408c2ecf20Sopenharmony_ci		.xres		= 1280,
2418c2ecf20Sopenharmony_ci		.yres		= 720,
2428c2ecf20Sopenharmony_ci		.pixclock	= 13426,
2438c2ecf20Sopenharmony_ci		.left_margin	= 192,
2448c2ecf20Sopenharmony_ci		.right_margin	= 64,
2458c2ecf20Sopenharmony_ci		.upper_margin	= 22,
2468c2ecf20Sopenharmony_ci		.lower_margin	= 1,
2478c2ecf20Sopenharmony_ci		.hsync_len	= 136,
2488c2ecf20Sopenharmony_ci		.vsync_len	= 3,
2498c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2508c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2518c2ecf20Sopenharmony_ci	},
2528c2ecf20Sopenharmony_ci	{
2538c2ecf20Sopenharmony_ci		.refresh	= 60,
2548c2ecf20Sopenharmony_ci		.xres		= 1280,
2558c2ecf20Sopenharmony_ci		.yres		= 1024,
2568c2ecf20Sopenharmony_ci		.pixclock	= 9375,
2578c2ecf20Sopenharmony_ci		.left_margin	= 38,
2588c2ecf20Sopenharmony_ci		.right_margin	= 128,
2598c2ecf20Sopenharmony_ci		.upper_margin	= 2,
2608c2ecf20Sopenharmony_ci		.lower_margin	= 7,
2618c2ecf20Sopenharmony_ci		.hsync_len	= 216,
2628c2ecf20Sopenharmony_ci		.vsync_len	= 37,
2638c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2648c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2658c2ecf20Sopenharmony_ci	},
2668c2ecf20Sopenharmony_ci	{
2678c2ecf20Sopenharmony_ci		.refresh	= 70,
2688c2ecf20Sopenharmony_ci		.xres		= 1280,
2698c2ecf20Sopenharmony_ci		.yres		= 1024,
2708c2ecf20Sopenharmony_ci		.pixclock	= 9380,
2718c2ecf20Sopenharmony_ci		.left_margin	= 6,
2728c2ecf20Sopenharmony_ci		.right_margin	= 6,
2738c2ecf20Sopenharmony_ci		.upper_margin	= 4,
2748c2ecf20Sopenharmony_ci		.lower_margin	= 4,
2758c2ecf20Sopenharmony_ci		.hsync_len	= 60,
2768c2ecf20Sopenharmony_ci		.vsync_len	= 94,
2778c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2788c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2798c2ecf20Sopenharmony_ci	},
2808c2ecf20Sopenharmony_ci	{
2818c2ecf20Sopenharmony_ci		.refresh	= 75,
2828c2ecf20Sopenharmony_ci		.xres		= 1280,
2838c2ecf20Sopenharmony_ci		.yres		= 1024,
2848c2ecf20Sopenharmony_ci		.pixclock	= 9380,
2858c2ecf20Sopenharmony_ci		.left_margin	= 6,
2868c2ecf20Sopenharmony_ci		.right_margin	= 6,
2878c2ecf20Sopenharmony_ci		.upper_margin	= 4,
2888c2ecf20Sopenharmony_ci		.lower_margin	= 4,
2898c2ecf20Sopenharmony_ci		.hsync_len	= 60,
2908c2ecf20Sopenharmony_ci		.vsync_len	= 15,
2918c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
2928c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
2938c2ecf20Sopenharmony_ci	},
2948c2ecf20Sopenharmony_ci	{
2958c2ecf20Sopenharmony_ci		.refresh	= 60,
2968c2ecf20Sopenharmony_ci		.xres		= 1920,
2978c2ecf20Sopenharmony_ci		.yres		= 1080,
2988c2ecf20Sopenharmony_ci		.pixclock	= 5787,
2998c2ecf20Sopenharmony_ci		.left_margin	= 328,
3008c2ecf20Sopenharmony_ci		.right_margin	= 120,
3018c2ecf20Sopenharmony_ci		.upper_margin	= 34,
3028c2ecf20Sopenharmony_ci		.lower_margin	= 1,
3038c2ecf20Sopenharmony_ci		.hsync_len	= 208,
3048c2ecf20Sopenharmony_ci		.vsync_len	= 3,
3058c2ecf20Sopenharmony_ci		.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
3068c2ecf20Sopenharmony_ci		.vmode		= FB_VMODE_NONINTERLACED
3078c2ecf20Sopenharmony_ci	},
3088c2ecf20Sopenharmony_ci};
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic char *fb_mode;
3118c2ecf20Sopenharmony_cistatic unsigned long default_bpp = 32;
3128c2ecf20Sopenharmony_cistatic enum fsl_diu_monitor_port monitor_port;
3138c2ecf20Sopenharmony_cistatic char *monitor_string;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci#if defined(CONFIG_NOT_COHERENT_CACHE)
3168c2ecf20Sopenharmony_cistatic u8 *coherence_data;
3178c2ecf20Sopenharmony_cistatic size_t coherence_data_size;
3188c2ecf20Sopenharmony_cistatic unsigned int d_cache_line_size;
3198c2ecf20Sopenharmony_ci#endif
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(diu_lock);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cienum mfb_index {
3248c2ecf20Sopenharmony_ci	PLANE0 = 0,	/* Plane 0, only one AOI that fills the screen */
3258c2ecf20Sopenharmony_ci	PLANE1_AOI0,	/* Plane 1, first AOI */
3268c2ecf20Sopenharmony_ci	PLANE1_AOI1,	/* Plane 1, second AOI */
3278c2ecf20Sopenharmony_ci	PLANE2_AOI0,	/* Plane 2, first AOI */
3288c2ecf20Sopenharmony_ci	PLANE2_AOI1,	/* Plane 2, second AOI */
3298c2ecf20Sopenharmony_ci};
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistruct mfb_info {
3328c2ecf20Sopenharmony_ci	enum mfb_index index;
3338c2ecf20Sopenharmony_ci	char *id;
3348c2ecf20Sopenharmony_ci	int registered;
3358c2ecf20Sopenharmony_ci	unsigned long pseudo_palette[16];
3368c2ecf20Sopenharmony_ci	struct diu_ad *ad;
3378c2ecf20Sopenharmony_ci	unsigned char g_alpha;
3388c2ecf20Sopenharmony_ci	unsigned int count;
3398c2ecf20Sopenharmony_ci	int x_aoi_d;		/* aoi display x offset to physical screen */
3408c2ecf20Sopenharmony_ci	int y_aoi_d;		/* aoi display y offset to physical screen */
3418c2ecf20Sopenharmony_ci	struct fsl_diu_data *parent;
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/**
3458c2ecf20Sopenharmony_ci * struct fsl_diu_data - per-DIU data structure
3468c2ecf20Sopenharmony_ci * @dma_addr: DMA address of this structure
3478c2ecf20Sopenharmony_ci * @fsl_diu_info: fb_info objects, one per AOI
3488c2ecf20Sopenharmony_ci * @dev_attr: sysfs structure
3498c2ecf20Sopenharmony_ci * @irq: IRQ
3508c2ecf20Sopenharmony_ci * @monitor_port: the monitor port this DIU is connected to
3518c2ecf20Sopenharmony_ci * @diu_reg: pointer to the DIU hardware registers
3528c2ecf20Sopenharmony_ci * @reg_lock: spinlock for register access
3538c2ecf20Sopenharmony_ci * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI
3548c2ecf20Sopenharmony_ci * dummy_ad: DIU Area Descriptor for the dummy AOI
3558c2ecf20Sopenharmony_ci * @ad[]: Area Descriptors for each real AOI
3568c2ecf20Sopenharmony_ci * @gamma: gamma color table
3578c2ecf20Sopenharmony_ci * @cursor: hardware cursor data
3588c2ecf20Sopenharmony_ci * @blank_cursor: blank cursor for hiding cursor
3598c2ecf20Sopenharmony_ci * @next_cursor: scratch space to build load cursor
3608c2ecf20Sopenharmony_ci * @edid_data: EDID information buffer
3618c2ecf20Sopenharmony_ci * @has_edid: whether or not the EDID buffer is valid
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * This data structure must be allocated with 32-byte alignment, so that the
3648c2ecf20Sopenharmony_ci * internal fields can be aligned properly.
3658c2ecf20Sopenharmony_ci */
3668c2ecf20Sopenharmony_cistruct fsl_diu_data {
3678c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
3688c2ecf20Sopenharmony_ci	struct fb_info fsl_diu_info[NUM_AOIS];
3698c2ecf20Sopenharmony_ci	struct mfb_info mfb[NUM_AOIS];
3708c2ecf20Sopenharmony_ci	struct device_attribute dev_attr;
3718c2ecf20Sopenharmony_ci	unsigned int irq;
3728c2ecf20Sopenharmony_ci	enum fsl_diu_monitor_port monitor_port;
3738c2ecf20Sopenharmony_ci	struct diu __iomem *diu_reg;
3748c2ecf20Sopenharmony_ci	spinlock_t reg_lock;
3758c2ecf20Sopenharmony_ci	u8 dummy_aoi[4 * 4 * 4];
3768c2ecf20Sopenharmony_ci	struct diu_ad dummy_ad __aligned(8);
3778c2ecf20Sopenharmony_ci	struct diu_ad ad[NUM_AOIS] __aligned(8);
3788c2ecf20Sopenharmony_ci	u8 gamma[256 * 3] __aligned(32);
3798c2ecf20Sopenharmony_ci	/* It's easier to parse the cursor data as little-endian */
3808c2ecf20Sopenharmony_ci	__le16 cursor[MAX_CURS * MAX_CURS] __aligned(32);
3818c2ecf20Sopenharmony_ci	/* Blank cursor data -- used to hide the cursor */
3828c2ecf20Sopenharmony_ci	__le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32);
3838c2ecf20Sopenharmony_ci	/* Scratch cursor data -- used to build new cursor */
3848c2ecf20Sopenharmony_ci	__le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);
3858c2ecf20Sopenharmony_ci	uint8_t edid_data[EDID_LENGTH];
3868c2ecf20Sopenharmony_ci	bool has_edid;
3878c2ecf20Sopenharmony_ci} __aligned(32);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci/* Determine the DMA address of a member of the fsl_diu_data structure */
3908c2ecf20Sopenharmony_ci#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic const struct mfb_info mfb_template[] = {
3938c2ecf20Sopenharmony_ci	{
3948c2ecf20Sopenharmony_ci		.index = PLANE0,
3958c2ecf20Sopenharmony_ci		.id = "Panel0",
3968c2ecf20Sopenharmony_ci		.registered = 0,
3978c2ecf20Sopenharmony_ci		.count = 0,
3988c2ecf20Sopenharmony_ci		.x_aoi_d = 0,
3998c2ecf20Sopenharmony_ci		.y_aoi_d = 0,
4008c2ecf20Sopenharmony_ci	},
4018c2ecf20Sopenharmony_ci	{
4028c2ecf20Sopenharmony_ci		.index = PLANE1_AOI0,
4038c2ecf20Sopenharmony_ci		.id = "Panel1 AOI0",
4048c2ecf20Sopenharmony_ci		.registered = 0,
4058c2ecf20Sopenharmony_ci		.g_alpha = 0xff,
4068c2ecf20Sopenharmony_ci		.count = 0,
4078c2ecf20Sopenharmony_ci		.x_aoi_d = 0,
4088c2ecf20Sopenharmony_ci		.y_aoi_d = 0,
4098c2ecf20Sopenharmony_ci	},
4108c2ecf20Sopenharmony_ci	{
4118c2ecf20Sopenharmony_ci		.index = PLANE1_AOI1,
4128c2ecf20Sopenharmony_ci		.id = "Panel1 AOI1",
4138c2ecf20Sopenharmony_ci		.registered = 0,
4148c2ecf20Sopenharmony_ci		.g_alpha = 0xff,
4158c2ecf20Sopenharmony_ci		.count = 0,
4168c2ecf20Sopenharmony_ci		.x_aoi_d = 0,
4178c2ecf20Sopenharmony_ci		.y_aoi_d = 480,
4188c2ecf20Sopenharmony_ci	},
4198c2ecf20Sopenharmony_ci	{
4208c2ecf20Sopenharmony_ci		.index = PLANE2_AOI0,
4218c2ecf20Sopenharmony_ci		.id = "Panel2 AOI0",
4228c2ecf20Sopenharmony_ci		.registered = 0,
4238c2ecf20Sopenharmony_ci		.g_alpha = 0xff,
4248c2ecf20Sopenharmony_ci		.count = 0,
4258c2ecf20Sopenharmony_ci		.x_aoi_d = 640,
4268c2ecf20Sopenharmony_ci		.y_aoi_d = 0,
4278c2ecf20Sopenharmony_ci	},
4288c2ecf20Sopenharmony_ci	{
4298c2ecf20Sopenharmony_ci		.index = PLANE2_AOI1,
4308c2ecf20Sopenharmony_ci		.id = "Panel2 AOI1",
4318c2ecf20Sopenharmony_ci		.registered = 0,
4328c2ecf20Sopenharmony_ci		.g_alpha = 0xff,
4338c2ecf20Sopenharmony_ci		.count = 0,
4348c2ecf20Sopenharmony_ci		.x_aoi_d = 640,
4358c2ecf20Sopenharmony_ci		.y_aoi_d = 480,
4368c2ecf20Sopenharmony_ci	},
4378c2ecf20Sopenharmony_ci};
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci#ifdef DEBUG
4408c2ecf20Sopenharmony_cistatic void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	mb();
4438c2ecf20Sopenharmony_ci	pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x palette=%08x "
4448c2ecf20Sopenharmony_ci		 "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "
4458c2ecf20Sopenharmony_ci		 "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "
4468c2ecf20Sopenharmony_ci		 "thresholds=%08x int_mask=%08x plut=%08x\n",
4478c2ecf20Sopenharmony_ci		 hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,
4488c2ecf20Sopenharmony_ci		 hw->palette, hw->cursor, hw->curs_pos, hw->diu_mode,
4498c2ecf20Sopenharmony_ci		 hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,
4508c2ecf20Sopenharmony_ci		 hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);
4518c2ecf20Sopenharmony_ci	rmb();
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci#endif
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/**
4568c2ecf20Sopenharmony_ci * fsl_diu_name_to_port - convert a port name to a monitor port enum
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns
4598c2ecf20Sopenharmony_ci * the enum fsl_diu_monitor_port that corresponds to that string.
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci * For compatibility with older versions, a number ("0", "1", or "2") is also
4628c2ecf20Sopenharmony_ci * supported.
4638c2ecf20Sopenharmony_ci *
4648c2ecf20Sopenharmony_ci * If the string is unknown, DVI is assumed.
4658c2ecf20Sopenharmony_ci *
4668c2ecf20Sopenharmony_ci * If the particular port is not supported by the platform, another port
4678c2ecf20Sopenharmony_ci * (platform-specific) is chosen instead.
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_cistatic enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;
4728c2ecf20Sopenharmony_ci	unsigned long val;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (s) {
4758c2ecf20Sopenharmony_ci		if (!kstrtoul(s, 10, &val) && (val <= 2))
4768c2ecf20Sopenharmony_ci			port = (enum fsl_diu_monitor_port) val;
4778c2ecf20Sopenharmony_ci		else if (strncmp(s, "lvds", 4) == 0)
4788c2ecf20Sopenharmony_ci			port = FSL_DIU_PORT_LVDS;
4798c2ecf20Sopenharmony_ci		else if (strncmp(s, "dlvds", 5) == 0)
4808c2ecf20Sopenharmony_ci			port = FSL_DIU_PORT_DLVDS;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (diu_ops.valid_monitor_port)
4848c2ecf20Sopenharmony_ci		port = diu_ops.valid_monitor_port(port);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	return port;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/*
4908c2ecf20Sopenharmony_ci * Workaround for failed writing desc register of planes.
4918c2ecf20Sopenharmony_ci * Needed with MPC5121 DIU rev 2.0 silicon.
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_cistatic void wr_reg_wa(u32 *reg, u32 val)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	do {
4968c2ecf20Sopenharmony_ci		out_be32(reg, val);
4978c2ecf20Sopenharmony_ci	} while (in_be32(reg) != val);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic void fsl_diu_enable_panel(struct fb_info *info)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
5038c2ecf20Sopenharmony_ci	struct diu_ad *ad = mfbi->ad;
5048c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
5058c2ecf20Sopenharmony_ci	struct diu __iomem *hw = data->diu_reg;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	switch (mfbi->index) {
5088c2ecf20Sopenharmony_ci	case PLANE0:
5098c2ecf20Sopenharmony_ci		wr_reg_wa(&hw->desc[0], ad->paddr);
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	case PLANE1_AOI0:
5128c2ecf20Sopenharmony_ci		cmfbi = &data->mfb[2];
5138c2ecf20Sopenharmony_ci		if (hw->desc[1] != ad->paddr) {	/* AOI0 closed */
5148c2ecf20Sopenharmony_ci			if (cmfbi->count > 0)	/* AOI1 open */
5158c2ecf20Sopenharmony_ci				ad->next_ad =
5168c2ecf20Sopenharmony_ci					cpu_to_le32(cmfbi->ad->paddr);
5178c2ecf20Sopenharmony_ci			else
5188c2ecf20Sopenharmony_ci				ad->next_ad = 0;
5198c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[1], ad->paddr);
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci		break;
5228c2ecf20Sopenharmony_ci	case PLANE2_AOI0:
5238c2ecf20Sopenharmony_ci		cmfbi = &data->mfb[4];
5248c2ecf20Sopenharmony_ci		if (hw->desc[2] != ad->paddr) {	/* AOI0 closed */
5258c2ecf20Sopenharmony_ci			if (cmfbi->count > 0)	/* AOI1 open */
5268c2ecf20Sopenharmony_ci				ad->next_ad =
5278c2ecf20Sopenharmony_ci					cpu_to_le32(cmfbi->ad->paddr);
5288c2ecf20Sopenharmony_ci			else
5298c2ecf20Sopenharmony_ci				ad->next_ad = 0;
5308c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[2], ad->paddr);
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci		break;
5338c2ecf20Sopenharmony_ci	case PLANE1_AOI1:
5348c2ecf20Sopenharmony_ci		pmfbi = &data->mfb[1];
5358c2ecf20Sopenharmony_ci		ad->next_ad = 0;
5368c2ecf20Sopenharmony_ci		if (hw->desc[1] == data->dummy_ad.paddr)
5378c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[1], ad->paddr);
5388c2ecf20Sopenharmony_ci		else					/* AOI0 open */
5398c2ecf20Sopenharmony_ci			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
5408c2ecf20Sopenharmony_ci		break;
5418c2ecf20Sopenharmony_ci	case PLANE2_AOI1:
5428c2ecf20Sopenharmony_ci		pmfbi = &data->mfb[3];
5438c2ecf20Sopenharmony_ci		ad->next_ad = 0;
5448c2ecf20Sopenharmony_ci		if (hw->desc[2] == data->dummy_ad.paddr)
5458c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[2], ad->paddr);
5468c2ecf20Sopenharmony_ci		else				/* AOI0 was open */
5478c2ecf20Sopenharmony_ci			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
5488c2ecf20Sopenharmony_ci		break;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic void fsl_diu_disable_panel(struct fb_info *info)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
5558c2ecf20Sopenharmony_ci	struct diu_ad *ad = mfbi->ad;
5568c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
5578c2ecf20Sopenharmony_ci	struct diu __iomem *hw = data->diu_reg;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	switch (mfbi->index) {
5608c2ecf20Sopenharmony_ci	case PLANE0:
5618c2ecf20Sopenharmony_ci		wr_reg_wa(&hw->desc[0], 0);
5628c2ecf20Sopenharmony_ci		break;
5638c2ecf20Sopenharmony_ci	case PLANE1_AOI0:
5648c2ecf20Sopenharmony_ci		cmfbi = &data->mfb[2];
5658c2ecf20Sopenharmony_ci		if (cmfbi->count > 0)	/* AOI1 is open */
5668c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
5678c2ecf20Sopenharmony_ci					/* move AOI1 to the first */
5688c2ecf20Sopenharmony_ci		else			/* AOI1 was closed */
5698c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
5708c2ecf20Sopenharmony_ci					/* close AOI 0 */
5718c2ecf20Sopenharmony_ci		break;
5728c2ecf20Sopenharmony_ci	case PLANE2_AOI0:
5738c2ecf20Sopenharmony_ci		cmfbi = &data->mfb[4];
5748c2ecf20Sopenharmony_ci		if (cmfbi->count > 0)	/* AOI1 is open */
5758c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
5768c2ecf20Sopenharmony_ci					/* move AOI1 to the first */
5778c2ecf20Sopenharmony_ci		else			/* AOI1 was closed */
5788c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
5798c2ecf20Sopenharmony_ci					/* close AOI 0 */
5808c2ecf20Sopenharmony_ci		break;
5818c2ecf20Sopenharmony_ci	case PLANE1_AOI1:
5828c2ecf20Sopenharmony_ci		pmfbi = &data->mfb[1];
5838c2ecf20Sopenharmony_ci		if (hw->desc[1] != ad->paddr) {
5848c2ecf20Sopenharmony_ci				/* AOI1 is not the first in the chain */
5858c2ecf20Sopenharmony_ci			if (pmfbi->count > 0)
5868c2ecf20Sopenharmony_ci					/* AOI0 is open, must be the first */
5878c2ecf20Sopenharmony_ci				pmfbi->ad->next_ad = 0;
5888c2ecf20Sopenharmony_ci		} else			/* AOI1 is the first in the chain */
5898c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
5908c2ecf20Sopenharmony_ci					/* close AOI 1 */
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci	case PLANE2_AOI1:
5938c2ecf20Sopenharmony_ci		pmfbi = &data->mfb[3];
5948c2ecf20Sopenharmony_ci		if (hw->desc[2] != ad->paddr) {
5958c2ecf20Sopenharmony_ci				/* AOI1 is not the first in the chain */
5968c2ecf20Sopenharmony_ci			if (pmfbi->count > 0)
5978c2ecf20Sopenharmony_ci				/* AOI0 is open, must be the first */
5988c2ecf20Sopenharmony_ci				pmfbi->ad->next_ad = 0;
5998c2ecf20Sopenharmony_ci		} else		/* AOI1 is the first in the chain */
6008c2ecf20Sopenharmony_ci			wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
6018c2ecf20Sopenharmony_ci				/* close AOI 1 */
6028c2ecf20Sopenharmony_ci		break;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic void enable_lcdc(struct fb_info *info)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
6098c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
6108c2ecf20Sopenharmony_ci	struct diu __iomem *hw = data->diu_reg;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	out_be32(&hw->diu_mode, MFB_MODE1);
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic void disable_lcdc(struct fb_info *info)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
6188c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
6198c2ecf20Sopenharmony_ci	struct diu __iomem *hw = data->diu_reg;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	out_be32(&hw->diu_mode, 0);
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic void adjust_aoi_size_position(struct fb_var_screeninfo *var,
6258c2ecf20Sopenharmony_ci				struct fb_info *info)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
6288c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
6298c2ecf20Sopenharmony_ci	int available_height, upper_aoi_bottom;
6308c2ecf20Sopenharmony_ci	enum mfb_index index = mfbi->index;
6318c2ecf20Sopenharmony_ci	int lower_aoi_is_open, upper_aoi_is_open;
6328c2ecf20Sopenharmony_ci	__u32 base_plane_width, base_plane_height, upper_aoi_height;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	base_plane_width = data->fsl_diu_info[0].var.xres;
6358c2ecf20Sopenharmony_ci	base_plane_height = data->fsl_diu_info[0].var.yres;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if (mfbi->x_aoi_d < 0)
6388c2ecf20Sopenharmony_ci		mfbi->x_aoi_d = 0;
6398c2ecf20Sopenharmony_ci	if (mfbi->y_aoi_d < 0)
6408c2ecf20Sopenharmony_ci		mfbi->y_aoi_d = 0;
6418c2ecf20Sopenharmony_ci	switch (index) {
6428c2ecf20Sopenharmony_ci	case PLANE0:
6438c2ecf20Sopenharmony_ci		if (mfbi->x_aoi_d != 0)
6448c2ecf20Sopenharmony_ci			mfbi->x_aoi_d = 0;
6458c2ecf20Sopenharmony_ci		if (mfbi->y_aoi_d != 0)
6468c2ecf20Sopenharmony_ci			mfbi->y_aoi_d = 0;
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci	case PLANE1_AOI0:
6498c2ecf20Sopenharmony_ci	case PLANE2_AOI0:
6508c2ecf20Sopenharmony_ci		lower_aoi_mfbi = data->fsl_diu_info[index+1].par;
6518c2ecf20Sopenharmony_ci		lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
6528c2ecf20Sopenharmony_ci		if (var->xres > base_plane_width)
6538c2ecf20Sopenharmony_ci			var->xres = base_plane_width;
6548c2ecf20Sopenharmony_ci		if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
6558c2ecf20Sopenharmony_ci			mfbi->x_aoi_d = base_plane_width - var->xres;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci		if (lower_aoi_is_open)
6588c2ecf20Sopenharmony_ci			available_height = lower_aoi_mfbi->y_aoi_d;
6598c2ecf20Sopenharmony_ci		else
6608c2ecf20Sopenharmony_ci			available_height = base_plane_height;
6618c2ecf20Sopenharmony_ci		if (var->yres > available_height)
6628c2ecf20Sopenharmony_ci			var->yres = available_height;
6638c2ecf20Sopenharmony_ci		if ((mfbi->y_aoi_d + var->yres) > available_height)
6648c2ecf20Sopenharmony_ci			mfbi->y_aoi_d = available_height - var->yres;
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci	case PLANE1_AOI1:
6678c2ecf20Sopenharmony_ci	case PLANE2_AOI1:
6688c2ecf20Sopenharmony_ci		upper_aoi_mfbi = data->fsl_diu_info[index-1].par;
6698c2ecf20Sopenharmony_ci		upper_aoi_height = data->fsl_diu_info[index-1].var.yres;
6708c2ecf20Sopenharmony_ci		upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
6718c2ecf20Sopenharmony_ci		upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
6728c2ecf20Sopenharmony_ci		if (var->xres > base_plane_width)
6738c2ecf20Sopenharmony_ci			var->xres = base_plane_width;
6748c2ecf20Sopenharmony_ci		if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
6758c2ecf20Sopenharmony_ci			mfbi->x_aoi_d = base_plane_width - var->xres;
6768c2ecf20Sopenharmony_ci		if (mfbi->y_aoi_d < 0)
6778c2ecf20Sopenharmony_ci			mfbi->y_aoi_d = 0;
6788c2ecf20Sopenharmony_ci		if (upper_aoi_is_open) {
6798c2ecf20Sopenharmony_ci			if (mfbi->y_aoi_d < upper_aoi_bottom)
6808c2ecf20Sopenharmony_ci				mfbi->y_aoi_d = upper_aoi_bottom;
6818c2ecf20Sopenharmony_ci			available_height = base_plane_height
6828c2ecf20Sopenharmony_ci						- upper_aoi_bottom;
6838c2ecf20Sopenharmony_ci		} else
6848c2ecf20Sopenharmony_ci			available_height = base_plane_height;
6858c2ecf20Sopenharmony_ci		if (var->yres > available_height)
6868c2ecf20Sopenharmony_ci			var->yres = available_height;
6878c2ecf20Sopenharmony_ci		if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
6888c2ecf20Sopenharmony_ci			mfbi->y_aoi_d = base_plane_height - var->yres;
6898c2ecf20Sopenharmony_ci		break;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci/*
6938c2ecf20Sopenharmony_ci * Checks to see if the hardware supports the state requested by var passed
6948c2ecf20Sopenharmony_ci * in. This function does not alter the hardware state! If the var passed in
6958c2ecf20Sopenharmony_ci * is slightly off by what the hardware can support then we alter the var
6968c2ecf20Sopenharmony_ci * PASSED in to what we can do. If the hardware doesn't support mode change
6978c2ecf20Sopenharmony_ci * a -EINVAL will be returned by the upper layers.
6988c2ecf20Sopenharmony_ci */
6998c2ecf20Sopenharmony_cistatic int fsl_diu_check_var(struct fb_var_screeninfo *var,
7008c2ecf20Sopenharmony_ci				struct fb_info *info)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	if (var->xres_virtual < var->xres)
7038c2ecf20Sopenharmony_ci		var->xres_virtual = var->xres;
7048c2ecf20Sopenharmony_ci	if (var->yres_virtual < var->yres)
7058c2ecf20Sopenharmony_ci		var->yres_virtual = var->yres;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (var->xoffset + info->var.xres > info->var.xres_virtual)
7088c2ecf20Sopenharmony_ci		var->xoffset = info->var.xres_virtual - info->var.xres;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (var->yoffset + info->var.yres > info->var.yres_virtual)
7118c2ecf20Sopenharmony_ci		var->yoffset = info->var.yres_virtual - info->var.yres;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
7148c2ecf20Sopenharmony_ci	    (var->bits_per_pixel != 16))
7158c2ecf20Sopenharmony_ci		var->bits_per_pixel = default_bpp;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
7188c2ecf20Sopenharmony_ci	case 16:
7198c2ecf20Sopenharmony_ci		var->red.length = 5;
7208c2ecf20Sopenharmony_ci		var->red.offset = 11;
7218c2ecf20Sopenharmony_ci		var->red.msb_right = 0;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		var->green.length = 6;
7248c2ecf20Sopenharmony_ci		var->green.offset = 5;
7258c2ecf20Sopenharmony_ci		var->green.msb_right = 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		var->blue.length = 5;
7288c2ecf20Sopenharmony_ci		var->blue.offset = 0;
7298c2ecf20Sopenharmony_ci		var->blue.msb_right = 0;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		var->transp.length = 0;
7328c2ecf20Sopenharmony_ci		var->transp.offset = 0;
7338c2ecf20Sopenharmony_ci		var->transp.msb_right = 0;
7348c2ecf20Sopenharmony_ci		break;
7358c2ecf20Sopenharmony_ci	case 24:
7368c2ecf20Sopenharmony_ci		var->red.length = 8;
7378c2ecf20Sopenharmony_ci		var->red.offset = 0;
7388c2ecf20Sopenharmony_ci		var->red.msb_right = 0;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		var->green.length = 8;
7418c2ecf20Sopenharmony_ci		var->green.offset = 8;
7428c2ecf20Sopenharmony_ci		var->green.msb_right = 0;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		var->blue.length = 8;
7458c2ecf20Sopenharmony_ci		var->blue.offset = 16;
7468c2ecf20Sopenharmony_ci		var->blue.msb_right = 0;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		var->transp.length = 0;
7498c2ecf20Sopenharmony_ci		var->transp.offset = 0;
7508c2ecf20Sopenharmony_ci		var->transp.msb_right = 0;
7518c2ecf20Sopenharmony_ci		break;
7528c2ecf20Sopenharmony_ci	case 32:
7538c2ecf20Sopenharmony_ci		var->red.length = 8;
7548c2ecf20Sopenharmony_ci		var->red.offset = 16;
7558c2ecf20Sopenharmony_ci		var->red.msb_right = 0;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		var->green.length = 8;
7588c2ecf20Sopenharmony_ci		var->green.offset = 8;
7598c2ecf20Sopenharmony_ci		var->green.msb_right = 0;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		var->blue.length = 8;
7628c2ecf20Sopenharmony_ci		var->blue.offset = 0;
7638c2ecf20Sopenharmony_ci		var->blue.msb_right = 0;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		var->transp.length = 8;
7668c2ecf20Sopenharmony_ci		var->transp.offset = 24;
7678c2ecf20Sopenharmony_ci		var->transp.msb_right = 0;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		break;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	var->height = -1;
7738c2ecf20Sopenharmony_ci	var->width = -1;
7748c2ecf20Sopenharmony_ci	var->grayscale = 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Copy nonstd field to/from sync for fbset usage */
7778c2ecf20Sopenharmony_ci	var->sync |= var->nonstd;
7788c2ecf20Sopenharmony_ci	var->nonstd |= var->sync;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	adjust_aoi_size_position(var, info);
7818c2ecf20Sopenharmony_ci	return 0;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void set_fix(struct fb_info *info)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct fb_fix_screeninfo *fix = &info->fix;
7878c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
7888c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	strncpy(fix->id, mfbi->id, sizeof(fix->id));
7918c2ecf20Sopenharmony_ci	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
7928c2ecf20Sopenharmony_ci	fix->type = FB_TYPE_PACKED_PIXELS;
7938c2ecf20Sopenharmony_ci	fix->accel = FB_ACCEL_NONE;
7948c2ecf20Sopenharmony_ci	fix->visual = FB_VISUAL_TRUECOLOR;
7958c2ecf20Sopenharmony_ci	fix->xpanstep = 1;
7968c2ecf20Sopenharmony_ci	fix->ypanstep = 1;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic void update_lcdc(struct fb_info *info)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
8028c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
8038c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
8048c2ecf20Sopenharmony_ci	struct diu __iomem *hw;
8058c2ecf20Sopenharmony_ci	int i, j;
8068c2ecf20Sopenharmony_ci	u8 *gamma_table_base;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	u32 temp;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	hw = data->diu_reg;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (diu_ops.set_monitor_port)
8138c2ecf20Sopenharmony_ci		diu_ops.set_monitor_port(data->monitor_port);
8148c2ecf20Sopenharmony_ci	gamma_table_base = data->gamma;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/* Prep for DIU init  - gamma table, cursor table */
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	for (i = 0; i <= 2; i++)
8198c2ecf20Sopenharmony_ci		for (j = 0; j <= 255; j++)
8208c2ecf20Sopenharmony_ci			*gamma_table_base++ = j;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (diu_ops.set_gamma_table)
8238c2ecf20Sopenharmony_ci		diu_ops.set_gamma_table(data->monitor_port, data->gamma);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	disable_lcdc(info);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* Program DIU registers */
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	out_be32(&hw->gamma, DMA_ADDR(data, gamma));
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */
8328c2ecf20Sopenharmony_ci	out_be32(&hw->disp_size, (var->yres << 16) | var->xres);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* Horizontal and vertical configuration register */
8358c2ecf20Sopenharmony_ci	temp = var->left_margin << 22 | /* BP_H */
8368c2ecf20Sopenharmony_ci	       var->hsync_len << 11 |   /* PW_H */
8378c2ecf20Sopenharmony_ci	       var->right_margin;       /* FP_H */
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	out_be32(&hw->hsyn_para, temp);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	temp = var->upper_margin << 22 | /* BP_V */
8428c2ecf20Sopenharmony_ci	       var->vsync_len << 11 |    /* PW_V  */
8438c2ecf20Sopenharmony_ci	       var->lower_margin;        /* FP_V  */
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	out_be32(&hw->vsyn_para, temp);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	diu_ops.set_pixel_clock(var->pixclock);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_MPC512x
8508c2ecf20Sopenharmony_ci	/*
8518c2ecf20Sopenharmony_ci	 * The PLUT register is defined differently on the MPC5121 than it
8528c2ecf20Sopenharmony_ci	 * is on other SOCs.  Unfortunately, there's no documentation that
8538c2ecf20Sopenharmony_ci	 * explains how it's supposed to be programmed, so for now, we leave
8548c2ecf20Sopenharmony_ci	 * it at the default value on the MPC5121.
8558c2ecf20Sopenharmony_ci	 *
8568c2ecf20Sopenharmony_ci	 * For other SOCs, program it for the highest priority, which will
8578c2ecf20Sopenharmony_ci	 * reduce the chance of underrun. Technically, we should scale the
8588c2ecf20Sopenharmony_ci	 * priority to match the screen resolution, but doing that properly
8598c2ecf20Sopenharmony_ci	 * requires delicate fine-tuning for each use-case.
8608c2ecf20Sopenharmony_ci	 */
8618c2ecf20Sopenharmony_ci	out_be32(&hw->plut, 0x01F5F666);
8628c2ecf20Sopenharmony_ci#endif
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* Enable the DIU */
8658c2ecf20Sopenharmony_ci	enable_lcdc(info);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic int map_video_memory(struct fb_info *info)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	u32 smem_len = info->fix.line_length * info->var.yres_virtual;
8718c2ecf20Sopenharmony_ci	void *p;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);
8748c2ecf20Sopenharmony_ci	if (!p) {
8758c2ecf20Sopenharmony_ci		dev_err(info->dev, "unable to allocate fb memory\n");
8768c2ecf20Sopenharmony_ci		return -ENOMEM;
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci	mutex_lock(&info->mm_lock);
8798c2ecf20Sopenharmony_ci	info->screen_base = p;
8808c2ecf20Sopenharmony_ci	info->fix.smem_start = virt_to_phys(info->screen_base);
8818c2ecf20Sopenharmony_ci	info->fix.smem_len = smem_len;
8828c2ecf20Sopenharmony_ci	mutex_unlock(&info->mm_lock);
8838c2ecf20Sopenharmony_ci	info->screen_size = info->fix.smem_len;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	return 0;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic void unmap_video_memory(struct fb_info *info)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	void *p = info->screen_base;
8918c2ecf20Sopenharmony_ci	size_t l = info->fix.smem_len;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	mutex_lock(&info->mm_lock);
8948c2ecf20Sopenharmony_ci	info->screen_base = NULL;
8958c2ecf20Sopenharmony_ci	info->fix.smem_start = 0;
8968c2ecf20Sopenharmony_ci	info->fix.smem_len = 0;
8978c2ecf20Sopenharmony_ci	mutex_unlock(&info->mm_lock);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (p)
9008c2ecf20Sopenharmony_ci		free_pages_exact(p, l);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/*
9048c2ecf20Sopenharmony_ci * Using the fb_var_screeninfo in fb_info we set the aoi of this
9058c2ecf20Sopenharmony_ci * particular framebuffer. It is a light version of fsl_diu_set_par.
9068c2ecf20Sopenharmony_ci */
9078c2ecf20Sopenharmony_cistatic int fsl_diu_set_aoi(struct fb_info *info)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
9108c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
9118c2ecf20Sopenharmony_ci	struct diu_ad *ad = mfbi->ad;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/* AOI should not be greater than display size */
9148c2ecf20Sopenharmony_ci	ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
9158c2ecf20Sopenharmony_ci	ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
9168c2ecf20Sopenharmony_ci	return 0;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci/**
9208c2ecf20Sopenharmony_ci * fsl_diu_get_pixel_format: return the pixel format for a given color depth
9218c2ecf20Sopenharmony_ci *
9228c2ecf20Sopenharmony_ci * The pixel format is a 32-bit value that determine which bits in each
9238c2ecf20Sopenharmony_ci * pixel are to be used for each color.  This is the default function used
9248c2ecf20Sopenharmony_ci * if the platform does not define its own version.
9258c2ecf20Sopenharmony_ci */
9268c2ecf20Sopenharmony_cistatic u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci#define PF_BYTE_F		0x10000000
9298c2ecf20Sopenharmony_ci#define PF_ALPHA_C_MASK		0x0E000000
9308c2ecf20Sopenharmony_ci#define PF_ALPHA_C_SHIFT	25
9318c2ecf20Sopenharmony_ci#define PF_BLUE_C_MASK		0x01800000
9328c2ecf20Sopenharmony_ci#define PF_BLUE_C_SHIFT		23
9338c2ecf20Sopenharmony_ci#define PF_GREEN_C_MASK		0x00600000
9348c2ecf20Sopenharmony_ci#define PF_GREEN_C_SHIFT	21
9358c2ecf20Sopenharmony_ci#define PF_RED_C_MASK		0x00180000
9368c2ecf20Sopenharmony_ci#define PF_RED_C_SHIFT		19
9378c2ecf20Sopenharmony_ci#define PF_PALETTE		0x00040000
9388c2ecf20Sopenharmony_ci#define PF_PIXEL_S_MASK		0x00030000
9398c2ecf20Sopenharmony_ci#define PF_PIXEL_S_SHIFT	16
9408c2ecf20Sopenharmony_ci#define PF_COMP_3_MASK		0x0000F000
9418c2ecf20Sopenharmony_ci#define PF_COMP_3_SHIFT		12
9428c2ecf20Sopenharmony_ci#define PF_COMP_2_MASK		0x00000F00
9438c2ecf20Sopenharmony_ci#define PF_COMP_2_SHIFT		8
9448c2ecf20Sopenharmony_ci#define PF_COMP_1_MASK		0x000000F0
9458c2ecf20Sopenharmony_ci#define PF_COMP_1_SHIFT		4
9468c2ecf20Sopenharmony_ci#define PF_COMP_0_MASK		0x0000000F
9478c2ecf20Sopenharmony_ci#define PF_COMP_0_SHIFT		0
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \
9508c2ecf20Sopenharmony_ci	cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \
9518c2ecf20Sopenharmony_ci	(blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \
9528c2ecf20Sopenharmony_ci	(red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \
9538c2ecf20Sopenharmony_ci	(c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \
9548c2ecf20Sopenharmony_ci	(c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	switch (bits_per_pixel) {
9578c2ecf20Sopenharmony_ci	case 32:
9588c2ecf20Sopenharmony_ci		/* 0x88883316 */
9598c2ecf20Sopenharmony_ci		return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);
9608c2ecf20Sopenharmony_ci	case 24:
9618c2ecf20Sopenharmony_ci		/* 0x88082219 */
9628c2ecf20Sopenharmony_ci		return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);
9638c2ecf20Sopenharmony_ci	case 16:
9648c2ecf20Sopenharmony_ci		/* 0x65053118 */
9658c2ecf20Sopenharmony_ci		return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);
9668c2ecf20Sopenharmony_ci	default:
9678c2ecf20Sopenharmony_ci		pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);
9688c2ecf20Sopenharmony_ci		return 0;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci/*
9738c2ecf20Sopenharmony_ci * Copies a cursor image from user space to the proper place in driver
9748c2ecf20Sopenharmony_ci * memory so that the hardware can display the cursor image.
9758c2ecf20Sopenharmony_ci *
9768c2ecf20Sopenharmony_ci * Cursor data is represented as a sequence of 'width' bits packed into bytes.
9778c2ecf20Sopenharmony_ci * That is, the first 8 bits are in the first byte, the second 8 bits in the
9788c2ecf20Sopenharmony_ci * second byte, and so on.  Therefore, the each row of the cursor is (width +
9798c2ecf20Sopenharmony_ci * 7) / 8 bytes of 'data'
9808c2ecf20Sopenharmony_ci *
9818c2ecf20Sopenharmony_ci * The DIU only supports cursors up to 32x32 (MAX_CURS).  We reject cursors
9828c2ecf20Sopenharmony_ci * larger than this, so we already know that 'width' <= 32.  Therefore, we can
9838c2ecf20Sopenharmony_ci * simplify our code by using a 32-bit big-endian integer ("line") to read in
9848c2ecf20Sopenharmony_ci * a single line of pixels, and only look at the top 'width' bits of that
9858c2ecf20Sopenharmony_ci * integer.
9868c2ecf20Sopenharmony_ci *
9878c2ecf20Sopenharmony_ci * This could result in an unaligned 32-bit read.  For example, if the cursor
9888c2ecf20Sopenharmony_ci * is 24x24, then the first three bytes of 'image' contain the pixel data for
9898c2ecf20Sopenharmony_ci * the top line of the cursor.  We do a 32-bit read of 'image', but we look
9908c2ecf20Sopenharmony_ci * only at the top 24 bits.  Then we increment 'image' by 3 bytes.  The next
9918c2ecf20Sopenharmony_ci * read is unaligned.  The only problem is that we might read past the end of
9928c2ecf20Sopenharmony_ci * 'image' by 1-3 bytes, but that should not cause any problems.
9938c2ecf20Sopenharmony_ci */
9948c2ecf20Sopenharmony_cistatic void fsl_diu_load_cursor_image(struct fb_info *info,
9958c2ecf20Sopenharmony_ci	const void *image, uint16_t bg, uint16_t fg,
9968c2ecf20Sopenharmony_ci	unsigned int width, unsigned int height)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
9998c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
10008c2ecf20Sopenharmony_ci	__le16 *cursor = data->cursor;
10018c2ecf20Sopenharmony_ci	__le16 _fg = cpu_to_le16(fg);
10028c2ecf20Sopenharmony_ci	__le16 _bg = cpu_to_le16(bg);
10038c2ecf20Sopenharmony_ci	unsigned int h, w;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	for (h = 0; h < height; h++) {
10068c2ecf20Sopenharmony_ci		uint32_t mask = 1 << 31;
10078c2ecf20Sopenharmony_ci		uint32_t line = be32_to_cpup(image);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci		for (w = 0; w < width; w++) {
10108c2ecf20Sopenharmony_ci			cursor[w] = (line & mask) ? _fg : _bg;
10118c2ecf20Sopenharmony_ci			mask >>= 1;
10128c2ecf20Sopenharmony_ci		}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		cursor += MAX_CURS;
10158c2ecf20Sopenharmony_ci		image += DIV_ROUND_UP(width, 8);
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci/*
10208c2ecf20Sopenharmony_ci * Set a hardware cursor.  The image data for the cursor is passed via the
10218c2ecf20Sopenharmony_ci * fb_cursor object.
10228c2ecf20Sopenharmony_ci */
10238c2ecf20Sopenharmony_cistatic int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
10268c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
10278c2ecf20Sopenharmony_ci	struct diu __iomem *hw = data->diu_reg;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
10308c2ecf20Sopenharmony_ci		return -EINVAL;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	/* The cursor size has changed */
10338c2ecf20Sopenharmony_ci	if (cursor->set & FB_CUR_SETSIZE) {
10348c2ecf20Sopenharmony_ci		/*
10358c2ecf20Sopenharmony_ci		 * The DIU cursor is a fixed size, so when we get this
10368c2ecf20Sopenharmony_ci		 * message, instead of resizing the cursor, we just clear
10378c2ecf20Sopenharmony_ci		 * all the image data, in expectation of new data.  However,
10388c2ecf20Sopenharmony_ci		 * in tests this control does not appear to be normally
10398c2ecf20Sopenharmony_ci		 * called.
10408c2ecf20Sopenharmony_ci		 */
10418c2ecf20Sopenharmony_ci		memset(data->cursor, 0, sizeof(data->cursor));
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	/* The cursor position has changed (cursor->image.dx|dy) */
10458c2ecf20Sopenharmony_ci	if (cursor->set & FB_CUR_SETPOS) {
10468c2ecf20Sopenharmony_ci		uint32_t xx, yy;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		yy = (cursor->image.dy - info->var.yoffset) & 0x7ff;
10498c2ecf20Sopenharmony_ci		xx = (cursor->image.dx - info->var.xoffset) & 0x7ff;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci		out_be32(&hw->curs_pos, yy << 16 | xx);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	/*
10558c2ecf20Sopenharmony_ci	 * FB_CUR_SETIMAGE - the cursor image has changed
10568c2ecf20Sopenharmony_ci	 * FB_CUR_SETCMAP  - the cursor colors has changed
10578c2ecf20Sopenharmony_ci	 * FB_CUR_SETSHAPE - the cursor bitmask has changed
10588c2ecf20Sopenharmony_ci	 */
10598c2ecf20Sopenharmony_ci	if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
10608c2ecf20Sopenharmony_ci		/*
10618c2ecf20Sopenharmony_ci		 * Determine the size of the cursor image data.  Normally,
10628c2ecf20Sopenharmony_ci		 * it's 8x16.
10638c2ecf20Sopenharmony_ci		 */
10648c2ecf20Sopenharmony_ci		unsigned int image_size =
10658c2ecf20Sopenharmony_ci			DIV_ROUND_UP(cursor->image.width, 8) *
10668c2ecf20Sopenharmony_ci			cursor->image.height;
10678c2ecf20Sopenharmony_ci		unsigned int image_words =
10688c2ecf20Sopenharmony_ci			DIV_ROUND_UP(image_size, sizeof(uint32_t));
10698c2ecf20Sopenharmony_ci		unsigned int bg_idx = cursor->image.bg_color;
10708c2ecf20Sopenharmony_ci		unsigned int fg_idx = cursor->image.fg_color;
10718c2ecf20Sopenharmony_ci		uint32_t *image, *source, *mask;
10728c2ecf20Sopenharmony_ci		uint16_t fg, bg;
10738c2ecf20Sopenharmony_ci		unsigned int i;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		if (info->state != FBINFO_STATE_RUNNING)
10768c2ecf20Sopenharmony_ci			return 0;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci		bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
10798c2ecf20Sopenharmony_ci		     ((info->cmap.green[bg_idx] & 0xf8) << 2) |
10808c2ecf20Sopenharmony_ci		     ((info->cmap.blue[bg_idx] & 0xf8) >> 3) |
10818c2ecf20Sopenharmony_ci		     1 << 15;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
10848c2ecf20Sopenharmony_ci		     ((info->cmap.green[fg_idx] & 0xf8) << 2) |
10858c2ecf20Sopenharmony_ci		     ((info->cmap.blue[fg_idx] & 0xf8) >> 3) |
10868c2ecf20Sopenharmony_ci		     1 << 15;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci		/* Use 32-bit operations on the data to improve performance */
10898c2ecf20Sopenharmony_ci		image = (uint32_t *)data->next_cursor;
10908c2ecf20Sopenharmony_ci		source = (uint32_t *)cursor->image.data;
10918c2ecf20Sopenharmony_ci		mask = (uint32_t *)cursor->mask;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		if (cursor->rop == ROP_XOR)
10948c2ecf20Sopenharmony_ci			for (i = 0; i < image_words; i++)
10958c2ecf20Sopenharmony_ci				image[i] = source[i] ^ mask[i];
10968c2ecf20Sopenharmony_ci		else
10978c2ecf20Sopenharmony_ci			for (i = 0; i < image_words; i++)
10988c2ecf20Sopenharmony_ci				image[i] = source[i] & mask[i];
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci		fsl_diu_load_cursor_image(info, image, bg, fg,
11018c2ecf20Sopenharmony_ci			cursor->image.width, cursor->image.height);
11028c2ecf20Sopenharmony_ci	}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/*
11058c2ecf20Sopenharmony_ci	 * Show or hide the cursor.  The cursor data is always stored in the
11068c2ecf20Sopenharmony_ci	 * 'cursor' memory block, and the actual cursor position is always in
11078c2ecf20Sopenharmony_ci	 * the DIU's CURS_POS register.  To hide the cursor, we redirect the
11088c2ecf20Sopenharmony_ci	 * CURSOR register to a blank cursor.  The show the cursor, we
11098c2ecf20Sopenharmony_ci	 * redirect the CURSOR register to the real cursor data.
11108c2ecf20Sopenharmony_ci	 */
11118c2ecf20Sopenharmony_ci	if (cursor->enable)
11128c2ecf20Sopenharmony_ci		out_be32(&hw->cursor, DMA_ADDR(data, cursor));
11138c2ecf20Sopenharmony_ci	else
11148c2ecf20Sopenharmony_ci		out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor));
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	return 0;
11178c2ecf20Sopenharmony_ci}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci/*
11208c2ecf20Sopenharmony_ci * Using the fb_var_screeninfo in fb_info we set the resolution of this
11218c2ecf20Sopenharmony_ci * particular framebuffer. This function alters the fb_fix_screeninfo stored
11228c2ecf20Sopenharmony_ci * in fb_info. It does not alter var in fb_info since we are using that
11238c2ecf20Sopenharmony_ci * data. This means we depend on the data in var inside fb_info to be
11248c2ecf20Sopenharmony_ci * supported by the hardware. fsl_diu_check_var is always called before
11258c2ecf20Sopenharmony_ci * fsl_diu_set_par to ensure this.
11268c2ecf20Sopenharmony_ci */
11278c2ecf20Sopenharmony_cistatic int fsl_diu_set_par(struct fb_info *info)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	unsigned long len;
11308c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
11318c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
11328c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
11338c2ecf20Sopenharmony_ci	struct diu_ad *ad = mfbi->ad;
11348c2ecf20Sopenharmony_ci	struct diu __iomem *hw;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	hw = data->diu_reg;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	set_fix(info);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	len = info->var.yres_virtual * info->fix.line_length;
11418c2ecf20Sopenharmony_ci	/* Alloc & dealloc each time resolution/bpp change */
11428c2ecf20Sopenharmony_ci	if (len != info->fix.smem_len) {
11438c2ecf20Sopenharmony_ci		if (info->fix.smem_start)
11448c2ecf20Sopenharmony_ci			unmap_video_memory(info);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		/* Memory allocation for framebuffer */
11478c2ecf20Sopenharmony_ci		if (map_video_memory(info)) {
11488c2ecf20Sopenharmony_ci			dev_err(info->dev, "unable to allocate fb memory 1\n");
11498c2ecf20Sopenharmony_ci			return -ENOMEM;
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if (diu_ops.get_pixel_format)
11548c2ecf20Sopenharmony_ci		ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port,
11558c2ecf20Sopenharmony_ci						       var->bits_per_pixel);
11568c2ecf20Sopenharmony_ci	else
11578c2ecf20Sopenharmony_ci		ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	ad->addr    = cpu_to_le32(info->fix.smem_start);
11608c2ecf20Sopenharmony_ci	ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
11618c2ecf20Sopenharmony_ci				var->xres_virtual) | mfbi->g_alpha;
11628c2ecf20Sopenharmony_ci	/* AOI should not be greater than display size */
11638c2ecf20Sopenharmony_ci	ad->aoi_size 	= cpu_to_le32((var->yres << 16) | var->xres);
11648c2ecf20Sopenharmony_ci	ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
11658c2ecf20Sopenharmony_ci	ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	/* Disable chroma keying function */
11688c2ecf20Sopenharmony_ci	ad->ckmax_r = 0;
11698c2ecf20Sopenharmony_ci	ad->ckmax_g = 0;
11708c2ecf20Sopenharmony_ci	ad->ckmax_b = 0;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	ad->ckmin_r = 255;
11738c2ecf20Sopenharmony_ci	ad->ckmin_g = 255;
11748c2ecf20Sopenharmony_ci	ad->ckmin_b = 255;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (mfbi->index == PLANE0)
11778c2ecf20Sopenharmony_ci		update_lcdc(info);
11788c2ecf20Sopenharmony_ci	return 0;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cistatic inline __u32 CNVT_TOHW(__u32 val, __u32 width)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	return ((val << width) + 0x7FFF - val) >> 16;
11848c2ecf20Sopenharmony_ci}
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci/*
11878c2ecf20Sopenharmony_ci * Set a single color register. The values supplied have a 16 bit magnitude
11888c2ecf20Sopenharmony_ci * which needs to be scaled in this function for the hardware. Things to take
11898c2ecf20Sopenharmony_ci * into consideration are how many color registers, if any, are supported with
11908c2ecf20Sopenharmony_ci * the current color visual. With truecolor mode no color palettes are
11918c2ecf20Sopenharmony_ci * supported. Here a pseudo palette is created which we store the value in
11928c2ecf20Sopenharmony_ci * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
11938c2ecf20Sopenharmony_ci * color palette.
11948c2ecf20Sopenharmony_ci */
11958c2ecf20Sopenharmony_cistatic int fsl_diu_setcolreg(unsigned int regno, unsigned int red,
11968c2ecf20Sopenharmony_ci			     unsigned int green, unsigned int blue,
11978c2ecf20Sopenharmony_ci			     unsigned int transp, struct fb_info *info)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	int ret = 1;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	/*
12028c2ecf20Sopenharmony_ci	 * If greyscale is true, then we convert the RGB value
12038c2ecf20Sopenharmony_ci	 * to greyscale no matter what visual we are using.
12048c2ecf20Sopenharmony_ci	 */
12058c2ecf20Sopenharmony_ci	if (info->var.grayscale)
12068c2ecf20Sopenharmony_ci		red = green = blue = (19595 * red + 38470 * green +
12078c2ecf20Sopenharmony_ci				      7471 * blue) >> 16;
12088c2ecf20Sopenharmony_ci	switch (info->fix.visual) {
12098c2ecf20Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
12108c2ecf20Sopenharmony_ci		/*
12118c2ecf20Sopenharmony_ci		 * 16-bit True Colour.  We encode the RGB value
12128c2ecf20Sopenharmony_ci		 * according to the RGB bitfield information.
12138c2ecf20Sopenharmony_ci		 */
12148c2ecf20Sopenharmony_ci		if (regno < 16) {
12158c2ecf20Sopenharmony_ci			u32 *pal = info->pseudo_palette;
12168c2ecf20Sopenharmony_ci			u32 v;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci			red = CNVT_TOHW(red, info->var.red.length);
12198c2ecf20Sopenharmony_ci			green = CNVT_TOHW(green, info->var.green.length);
12208c2ecf20Sopenharmony_ci			blue = CNVT_TOHW(blue, info->var.blue.length);
12218c2ecf20Sopenharmony_ci			transp = CNVT_TOHW(transp, info->var.transp.length);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci			v = (red << info->var.red.offset) |
12248c2ecf20Sopenharmony_ci			    (green << info->var.green.offset) |
12258c2ecf20Sopenharmony_ci			    (blue << info->var.blue.offset) |
12268c2ecf20Sopenharmony_ci			    (transp << info->var.transp.offset);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci			pal[regno] = v;
12298c2ecf20Sopenharmony_ci			ret = 0;
12308c2ecf20Sopenharmony_ci		}
12318c2ecf20Sopenharmony_ci		break;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	return ret;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci/*
12388c2ecf20Sopenharmony_ci * Pan (or wrap, depending on the `vmode' field) the display using the
12398c2ecf20Sopenharmony_ci * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
12408c2ecf20Sopenharmony_ci * don't fit, return -EINVAL.
12418c2ecf20Sopenharmony_ci */
12428c2ecf20Sopenharmony_cistatic int fsl_diu_pan_display(struct fb_var_screeninfo *var,
12438c2ecf20Sopenharmony_ci			     struct fb_info *info)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	if ((info->var.xoffset == var->xoffset) &&
12468c2ecf20Sopenharmony_ci	    (info->var.yoffset == var->yoffset))
12478c2ecf20Sopenharmony_ci		return 0;	/* No change, do nothing */
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	if (var->xoffset + info->var.xres > info->var.xres_virtual
12508c2ecf20Sopenharmony_ci	    || var->yoffset + info->var.yres > info->var.yres_virtual)
12518c2ecf20Sopenharmony_ci		return -EINVAL;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	info->var.xoffset = var->xoffset;
12548c2ecf20Sopenharmony_ci	info->var.yoffset = var->yoffset;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	if (var->vmode & FB_VMODE_YWRAP)
12578c2ecf20Sopenharmony_ci		info->var.vmode |= FB_VMODE_YWRAP;
12588c2ecf20Sopenharmony_ci	else
12598c2ecf20Sopenharmony_ci		info->var.vmode &= ~FB_VMODE_YWRAP;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	fsl_diu_set_aoi(info);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	return 0;
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
12678c2ecf20Sopenharmony_ci		       unsigned long arg)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
12708c2ecf20Sopenharmony_ci	struct diu_ad *ad = mfbi->ad;
12718c2ecf20Sopenharmony_ci	struct mfb_chroma_key ck;
12728c2ecf20Sopenharmony_ci	unsigned char global_alpha;
12738c2ecf20Sopenharmony_ci	struct aoi_display_offset aoi_d;
12748c2ecf20Sopenharmony_ci	__u32 pix_fmt;
12758c2ecf20Sopenharmony_ci	void __user *buf = (void __user *)arg;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	if (!arg)
12788c2ecf20Sopenharmony_ci		return -EINVAL;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,
12818c2ecf20Sopenharmony_ci		_IOC_DIR(cmd) & _IOC_READ ? "R" : "",
12828c2ecf20Sopenharmony_ci		_IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",
12838c2ecf20Sopenharmony_ci		_IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	switch (cmd) {
12868c2ecf20Sopenharmony_ci	case MFB_SET_PIXFMT_OLD:
12878c2ecf20Sopenharmony_ci		dev_warn(info->dev,
12888c2ecf20Sopenharmony_ci			 "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",
12898c2ecf20Sopenharmony_ci			 MFB_SET_PIXFMT_OLD);
12908c2ecf20Sopenharmony_ci		fallthrough;
12918c2ecf20Sopenharmony_ci	case MFB_SET_PIXFMT:
12928c2ecf20Sopenharmony_ci		if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
12938c2ecf20Sopenharmony_ci			return -EFAULT;
12948c2ecf20Sopenharmony_ci		ad->pix_fmt = pix_fmt;
12958c2ecf20Sopenharmony_ci		break;
12968c2ecf20Sopenharmony_ci	case MFB_GET_PIXFMT_OLD:
12978c2ecf20Sopenharmony_ci		dev_warn(info->dev,
12988c2ecf20Sopenharmony_ci			 "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",
12998c2ecf20Sopenharmony_ci			 MFB_GET_PIXFMT_OLD);
13008c2ecf20Sopenharmony_ci		fallthrough;
13018c2ecf20Sopenharmony_ci	case MFB_GET_PIXFMT:
13028c2ecf20Sopenharmony_ci		pix_fmt = ad->pix_fmt;
13038c2ecf20Sopenharmony_ci		if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
13048c2ecf20Sopenharmony_ci			return -EFAULT;
13058c2ecf20Sopenharmony_ci		break;
13068c2ecf20Sopenharmony_ci	case MFB_SET_AOID:
13078c2ecf20Sopenharmony_ci		if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
13088c2ecf20Sopenharmony_ci			return -EFAULT;
13098c2ecf20Sopenharmony_ci		mfbi->x_aoi_d = aoi_d.x_aoi_d;
13108c2ecf20Sopenharmony_ci		mfbi->y_aoi_d = aoi_d.y_aoi_d;
13118c2ecf20Sopenharmony_ci		fsl_diu_check_var(&info->var, info);
13128c2ecf20Sopenharmony_ci		fsl_diu_set_aoi(info);
13138c2ecf20Sopenharmony_ci		break;
13148c2ecf20Sopenharmony_ci	case MFB_GET_AOID:
13158c2ecf20Sopenharmony_ci		aoi_d.x_aoi_d = mfbi->x_aoi_d;
13168c2ecf20Sopenharmony_ci		aoi_d.y_aoi_d = mfbi->y_aoi_d;
13178c2ecf20Sopenharmony_ci		if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
13188c2ecf20Sopenharmony_ci			return -EFAULT;
13198c2ecf20Sopenharmony_ci		break;
13208c2ecf20Sopenharmony_ci	case MFB_GET_ALPHA:
13218c2ecf20Sopenharmony_ci		global_alpha = mfbi->g_alpha;
13228c2ecf20Sopenharmony_ci		if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
13238c2ecf20Sopenharmony_ci			return -EFAULT;
13248c2ecf20Sopenharmony_ci		break;
13258c2ecf20Sopenharmony_ci	case MFB_SET_ALPHA:
13268c2ecf20Sopenharmony_ci		/* set panel information */
13278c2ecf20Sopenharmony_ci		if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
13288c2ecf20Sopenharmony_ci			return -EFAULT;
13298c2ecf20Sopenharmony_ci		ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
13308c2ecf20Sopenharmony_ci							(global_alpha & 0xff);
13318c2ecf20Sopenharmony_ci		mfbi->g_alpha = global_alpha;
13328c2ecf20Sopenharmony_ci		break;
13338c2ecf20Sopenharmony_ci	case MFB_SET_CHROMA_KEY:
13348c2ecf20Sopenharmony_ci		/* set panel winformation */
13358c2ecf20Sopenharmony_ci		if (copy_from_user(&ck, buf, sizeof(ck)))
13368c2ecf20Sopenharmony_ci			return -EFAULT;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		if (ck.enable &&
13398c2ecf20Sopenharmony_ci		   (ck.red_max < ck.red_min ||
13408c2ecf20Sopenharmony_ci		    ck.green_max < ck.green_min ||
13418c2ecf20Sopenharmony_ci		    ck.blue_max < ck.blue_min))
13428c2ecf20Sopenharmony_ci			return -EINVAL;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci		if (!ck.enable) {
13458c2ecf20Sopenharmony_ci			ad->ckmax_r = 0;
13468c2ecf20Sopenharmony_ci			ad->ckmax_g = 0;
13478c2ecf20Sopenharmony_ci			ad->ckmax_b = 0;
13488c2ecf20Sopenharmony_ci			ad->ckmin_r = 255;
13498c2ecf20Sopenharmony_ci			ad->ckmin_g = 255;
13508c2ecf20Sopenharmony_ci			ad->ckmin_b = 255;
13518c2ecf20Sopenharmony_ci		} else {
13528c2ecf20Sopenharmony_ci			ad->ckmax_r = ck.red_max;
13538c2ecf20Sopenharmony_ci			ad->ckmax_g = ck.green_max;
13548c2ecf20Sopenharmony_ci			ad->ckmax_b = ck.blue_max;
13558c2ecf20Sopenharmony_ci			ad->ckmin_r = ck.red_min;
13568c2ecf20Sopenharmony_ci			ad->ckmin_g = ck.green_min;
13578c2ecf20Sopenharmony_ci			ad->ckmin_b = ck.blue_min;
13588c2ecf20Sopenharmony_ci		}
13598c2ecf20Sopenharmony_ci		break;
13608c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_MPC512x
13618c2ecf20Sopenharmony_ci	case MFB_SET_GAMMA: {
13628c2ecf20Sopenharmony_ci		struct fsl_diu_data *data = mfbi->parent;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci		if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))
13658c2ecf20Sopenharmony_ci			return -EFAULT;
13668c2ecf20Sopenharmony_ci		setbits32(&data->diu_reg->gamma, 0); /* Force table reload */
13678c2ecf20Sopenharmony_ci		break;
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci	case MFB_GET_GAMMA: {
13708c2ecf20Sopenharmony_ci		struct fsl_diu_data *data = mfbi->parent;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci		if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))
13738c2ecf20Sopenharmony_ci			return -EFAULT;
13748c2ecf20Sopenharmony_ci		break;
13758c2ecf20Sopenharmony_ci	}
13768c2ecf20Sopenharmony_ci#endif
13778c2ecf20Sopenharmony_ci	default:
13788c2ecf20Sopenharmony_ci		dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
13798c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	return 0;
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cistatic inline void fsl_diu_enable_interrupts(struct fsl_diu_data *data)
13868c2ecf20Sopenharmony_ci{
13878c2ecf20Sopenharmony_ci	u32 int_mask = INT_UNDRUN; /* enable underrun detection */
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_NOT_COHERENT_CACHE))
13908c2ecf20Sopenharmony_ci		int_mask |= INT_VSYNC; /* enable vertical sync */
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	clrbits32(&data->diu_reg->int_mask, int_mask);
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci/* turn on fb if count == 1
13968c2ecf20Sopenharmony_ci */
13978c2ecf20Sopenharmony_cistatic int fsl_diu_open(struct fb_info *info, int user)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
14008c2ecf20Sopenharmony_ci	int res = 0;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	/* free boot splash memory on first /dev/fb0 open */
14038c2ecf20Sopenharmony_ci	if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)
14048c2ecf20Sopenharmony_ci		diu_ops.release_bootmem();
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	spin_lock(&diu_lock);
14078c2ecf20Sopenharmony_ci	mfbi->count++;
14088c2ecf20Sopenharmony_ci	if (mfbi->count == 1) {
14098c2ecf20Sopenharmony_ci		fsl_diu_check_var(&info->var, info);
14108c2ecf20Sopenharmony_ci		res = fsl_diu_set_par(info);
14118c2ecf20Sopenharmony_ci		if (res < 0)
14128c2ecf20Sopenharmony_ci			mfbi->count--;
14138c2ecf20Sopenharmony_ci		else {
14148c2ecf20Sopenharmony_ci			fsl_diu_enable_interrupts(mfbi->parent);
14158c2ecf20Sopenharmony_ci			fsl_diu_enable_panel(info);
14168c2ecf20Sopenharmony_ci		}
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	spin_unlock(&diu_lock);
14208c2ecf20Sopenharmony_ci	return res;
14218c2ecf20Sopenharmony_ci}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci/* turn off fb if count == 0
14248c2ecf20Sopenharmony_ci */
14258c2ecf20Sopenharmony_cistatic int fsl_diu_release(struct fb_info *info, int user)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
14288c2ecf20Sopenharmony_ci	int res = 0;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	spin_lock(&diu_lock);
14318c2ecf20Sopenharmony_ci	mfbi->count--;
14328c2ecf20Sopenharmony_ci	if (mfbi->count == 0) {
14338c2ecf20Sopenharmony_ci		struct fsl_diu_data *data = mfbi->parent;
14348c2ecf20Sopenharmony_ci		bool disable = true;
14358c2ecf20Sopenharmony_ci		int i;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		/* Disable interrupts only if all AOIs are closed */
14388c2ecf20Sopenharmony_ci		for (i = 0; i < NUM_AOIS; i++) {
14398c2ecf20Sopenharmony_ci			struct mfb_info *mi = data->fsl_diu_info[i].par;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci			if (mi->count)
14428c2ecf20Sopenharmony_ci				disable = false;
14438c2ecf20Sopenharmony_ci		}
14448c2ecf20Sopenharmony_ci		if (disable)
14458c2ecf20Sopenharmony_ci			out_be32(&data->diu_reg->int_mask, 0xffffffff);
14468c2ecf20Sopenharmony_ci		fsl_diu_disable_panel(info);
14478c2ecf20Sopenharmony_ci	}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	spin_unlock(&diu_lock);
14508c2ecf20Sopenharmony_ci	return res;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic const struct fb_ops fsl_diu_ops = {
14548c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
14558c2ecf20Sopenharmony_ci	.fb_check_var = fsl_diu_check_var,
14568c2ecf20Sopenharmony_ci	.fb_set_par = fsl_diu_set_par,
14578c2ecf20Sopenharmony_ci	.fb_setcolreg = fsl_diu_setcolreg,
14588c2ecf20Sopenharmony_ci	.fb_pan_display = fsl_diu_pan_display,
14598c2ecf20Sopenharmony_ci	.fb_fillrect = cfb_fillrect,
14608c2ecf20Sopenharmony_ci	.fb_copyarea = cfb_copyarea,
14618c2ecf20Sopenharmony_ci	.fb_imageblit = cfb_imageblit,
14628c2ecf20Sopenharmony_ci	.fb_ioctl = fsl_diu_ioctl,
14638c2ecf20Sopenharmony_ci	.fb_open = fsl_diu_open,
14648c2ecf20Sopenharmony_ci	.fb_release = fsl_diu_release,
14658c2ecf20Sopenharmony_ci	.fb_cursor = fsl_diu_cursor,
14668c2ecf20Sopenharmony_ci};
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_cistatic int install_fb(struct fb_info *info)
14698c2ecf20Sopenharmony_ci{
14708c2ecf20Sopenharmony_ci	int rc;
14718c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
14728c2ecf20Sopenharmony_ci	struct fsl_diu_data *data = mfbi->parent;
14738c2ecf20Sopenharmony_ci	const char *aoi_mode, *init_aoi_mode = "320x240";
14748c2ecf20Sopenharmony_ci	struct fb_videomode *db = fsl_diu_mode_db;
14758c2ecf20Sopenharmony_ci	unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
14768c2ecf20Sopenharmony_ci	int has_default_mode = 1;
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	info->var.activate = FB_ACTIVATE_NOW;
14798c2ecf20Sopenharmony_ci	info->fbops = &fsl_diu_ops;
14808c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |
14818c2ecf20Sopenharmony_ci		FBINFO_READS_FAST;
14828c2ecf20Sopenharmony_ci	info->pseudo_palette = mfbi->pseudo_palette;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	rc = fb_alloc_cmap(&info->cmap, 16, 0);
14858c2ecf20Sopenharmony_ci	if (rc)
14868c2ecf20Sopenharmony_ci		return rc;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	if (mfbi->index == PLANE0) {
14898c2ecf20Sopenharmony_ci		if (data->has_edid) {
14908c2ecf20Sopenharmony_ci			/* Now build modedb from EDID */
14918c2ecf20Sopenharmony_ci			fb_edid_to_monspecs(data->edid_data, &info->monspecs);
14928c2ecf20Sopenharmony_ci			fb_videomode_to_modelist(info->monspecs.modedb,
14938c2ecf20Sopenharmony_ci						 info->monspecs.modedb_len,
14948c2ecf20Sopenharmony_ci						 &info->modelist);
14958c2ecf20Sopenharmony_ci			db = info->monspecs.modedb;
14968c2ecf20Sopenharmony_ci			dbsize = info->monspecs.modedb_len;
14978c2ecf20Sopenharmony_ci		}
14988c2ecf20Sopenharmony_ci		aoi_mode = fb_mode;
14998c2ecf20Sopenharmony_ci	} else {
15008c2ecf20Sopenharmony_ci		aoi_mode = init_aoi_mode;
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci	rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,
15038c2ecf20Sopenharmony_ci			  default_bpp);
15048c2ecf20Sopenharmony_ci	if (!rc) {
15058c2ecf20Sopenharmony_ci		/*
15068c2ecf20Sopenharmony_ci		 * For plane 0 we continue and look into
15078c2ecf20Sopenharmony_ci		 * driver's internal modedb.
15088c2ecf20Sopenharmony_ci		 */
15098c2ecf20Sopenharmony_ci		if ((mfbi->index == PLANE0) && data->has_edid)
15108c2ecf20Sopenharmony_ci			has_default_mode = 0;
15118c2ecf20Sopenharmony_ci		else
15128c2ecf20Sopenharmony_ci			return -EINVAL;
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (!has_default_mode) {
15168c2ecf20Sopenharmony_ci		rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
15178c2ecf20Sopenharmony_ci			ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);
15188c2ecf20Sopenharmony_ci		if (rc)
15198c2ecf20Sopenharmony_ci			has_default_mode = 1;
15208c2ecf20Sopenharmony_ci	}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	/* Still not found, use preferred mode from database if any */
15238c2ecf20Sopenharmony_ci	if (!has_default_mode && info->monspecs.modedb) {
15248c2ecf20Sopenharmony_ci		struct fb_monspecs *specs = &info->monspecs;
15258c2ecf20Sopenharmony_ci		struct fb_videomode *modedb = &specs->modedb[0];
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci		/*
15288c2ecf20Sopenharmony_ci		 * Get preferred timing. If not found,
15298c2ecf20Sopenharmony_ci		 * first mode in database will be used.
15308c2ecf20Sopenharmony_ci		 */
15318c2ecf20Sopenharmony_ci		if (specs->misc & FB_MISC_1ST_DETAIL) {
15328c2ecf20Sopenharmony_ci			int i;
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci			for (i = 0; i < specs->modedb_len; i++) {
15358c2ecf20Sopenharmony_ci				if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
15368c2ecf20Sopenharmony_ci					modedb = &specs->modedb[i];
15378c2ecf20Sopenharmony_ci					break;
15388c2ecf20Sopenharmony_ci				}
15398c2ecf20Sopenharmony_ci			}
15408c2ecf20Sopenharmony_ci		}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci		info->var.bits_per_pixel = default_bpp;
15438c2ecf20Sopenharmony_ci		fb_videomode_to_var(&info->var, modedb);
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (fsl_diu_check_var(&info->var, info)) {
15478c2ecf20Sopenharmony_ci		dev_err(info->dev, "fsl_diu_check_var failed\n");
15488c2ecf20Sopenharmony_ci		unmap_video_memory(info);
15498c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
15508c2ecf20Sopenharmony_ci		return -EINVAL;
15518c2ecf20Sopenharmony_ci	}
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	if (register_framebuffer(info) < 0) {
15548c2ecf20Sopenharmony_ci		dev_err(info->dev, "register_framebuffer failed\n");
15558c2ecf20Sopenharmony_ci		unmap_video_memory(info);
15568c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
15578c2ecf20Sopenharmony_ci		return -EINVAL;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	mfbi->registered = 1;
15618c2ecf20Sopenharmony_ci	dev_info(info->dev, "%s registered successfully\n", mfbi->id);
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	return 0;
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_cistatic void uninstall_fb(struct fb_info *info)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct mfb_info *mfbi = info->par;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	if (!mfbi->registered)
15718c2ecf20Sopenharmony_ci		return;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
15748c2ecf20Sopenharmony_ci	unmap_video_memory(info);
15758c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	mfbi->registered = 0;
15788c2ecf20Sopenharmony_ci}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_cistatic irqreturn_t fsl_diu_isr(int irq, void *dev_id)
15818c2ecf20Sopenharmony_ci{
15828c2ecf20Sopenharmony_ci	struct diu __iomem *hw = dev_id;
15838c2ecf20Sopenharmony_ci	uint32_t status = in_be32(&hw->int_status);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	if (status) {
15868c2ecf20Sopenharmony_ci		/* This is the workaround for underrun */
15878c2ecf20Sopenharmony_ci		if (status & INT_UNDRUN) {
15888c2ecf20Sopenharmony_ci			out_be32(&hw->diu_mode, 0);
15898c2ecf20Sopenharmony_ci			udelay(1);
15908c2ecf20Sopenharmony_ci			out_be32(&hw->diu_mode, 1);
15918c2ecf20Sopenharmony_ci		}
15928c2ecf20Sopenharmony_ci#if defined(CONFIG_NOT_COHERENT_CACHE)
15938c2ecf20Sopenharmony_ci		else if (status & INT_VSYNC) {
15948c2ecf20Sopenharmony_ci			unsigned int i;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci			for (i = 0; i < coherence_data_size;
15978c2ecf20Sopenharmony_ci				i += d_cache_line_size)
15988c2ecf20Sopenharmony_ci				__asm__ __volatile__ (
15998c2ecf20Sopenharmony_ci					"dcbz 0, %[input]"
16008c2ecf20Sopenharmony_ci				::[input]"r"(&coherence_data[i]));
16018c2ecf20Sopenharmony_ci		}
16028c2ecf20Sopenharmony_ci#endif
16038c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci	return IRQ_NONE;
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
16098c2ecf20Sopenharmony_ci/*
16108c2ecf20Sopenharmony_ci * Power management hooks. Note that we won't be called from IRQ context,
16118c2ecf20Sopenharmony_ci * unlike the blank functions above, so we may sleep.
16128c2ecf20Sopenharmony_ci */
16138c2ecf20Sopenharmony_cistatic int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
16148c2ecf20Sopenharmony_ci{
16158c2ecf20Sopenharmony_ci	struct fsl_diu_data *data;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	data = dev_get_drvdata(&ofdev->dev);
16188c2ecf20Sopenharmony_ci	disable_lcdc(data->fsl_diu_info);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	return 0;
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic int fsl_diu_resume(struct platform_device *ofdev)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	struct fsl_diu_data *data;
16268c2ecf20Sopenharmony_ci	unsigned int i;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	data = dev_get_drvdata(&ofdev->dev);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	fsl_diu_enable_interrupts(data);
16318c2ecf20Sopenharmony_ci	update_lcdc(data->fsl_diu_info);
16328c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_AOIS; i++) {
16338c2ecf20Sopenharmony_ci		if (data->mfb[i].count)
16348c2ecf20Sopenharmony_ci			fsl_diu_enable_panel(&data->fsl_diu_info[i]);
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	return 0;
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci#else
16418c2ecf20Sopenharmony_ci#define fsl_diu_suspend NULL
16428c2ecf20Sopenharmony_ci#define fsl_diu_resume NULL
16438c2ecf20Sopenharmony_ci#endif				/* CONFIG_PM */
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cistatic ssize_t store_monitor(struct device *device,
16468c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	enum fsl_diu_monitor_port old_monitor_port;
16498c2ecf20Sopenharmony_ci	struct fsl_diu_data *data =
16508c2ecf20Sopenharmony_ci		container_of(attr, struct fsl_diu_data, dev_attr);
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	old_monitor_port = data->monitor_port;
16538c2ecf20Sopenharmony_ci	data->monitor_port = fsl_diu_name_to_port(buf);
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	if (old_monitor_port != data->monitor_port) {
16568c2ecf20Sopenharmony_ci		/* All AOIs need adjust pixel format
16578c2ecf20Sopenharmony_ci		 * fsl_diu_set_par only change the pixsel format here
16588c2ecf20Sopenharmony_ci		 * unlikely to fail. */
16598c2ecf20Sopenharmony_ci		unsigned int i;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci		for (i=0; i < NUM_AOIS; i++)
16628c2ecf20Sopenharmony_ci			fsl_diu_set_par(&data->fsl_diu_info[i]);
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci	return count;
16658c2ecf20Sopenharmony_ci}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_cistatic ssize_t show_monitor(struct device *device,
16688c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
16698c2ecf20Sopenharmony_ci{
16708c2ecf20Sopenharmony_ci	struct fsl_diu_data *data =
16718c2ecf20Sopenharmony_ci		container_of(attr, struct fsl_diu_data, dev_attr);
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	switch (data->monitor_port) {
16748c2ecf20Sopenharmony_ci	case FSL_DIU_PORT_DVI:
16758c2ecf20Sopenharmony_ci		return sprintf(buf, "DVI\n");
16768c2ecf20Sopenharmony_ci	case FSL_DIU_PORT_LVDS:
16778c2ecf20Sopenharmony_ci		return sprintf(buf, "Single-link LVDS\n");
16788c2ecf20Sopenharmony_ci	case FSL_DIU_PORT_DLVDS:
16798c2ecf20Sopenharmony_ci		return sprintf(buf, "Dual-link LVDS\n");
16808c2ecf20Sopenharmony_ci	}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	return 0;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic int fsl_diu_probe(struct platform_device *pdev)
16868c2ecf20Sopenharmony_ci{
16878c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
16888c2ecf20Sopenharmony_ci	struct mfb_info *mfbi;
16898c2ecf20Sopenharmony_ci	struct fsl_diu_data *data;
16908c2ecf20Sopenharmony_ci	dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
16918c2ecf20Sopenharmony_ci	const void *prop;
16928c2ecf20Sopenharmony_ci	unsigned int i;
16938c2ecf20Sopenharmony_ci	int ret;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
16968c2ecf20Sopenharmony_ci				   &dma_addr, GFP_DMA | __GFP_ZERO);
16978c2ecf20Sopenharmony_ci	if (!data)
16988c2ecf20Sopenharmony_ci		return -ENOMEM;
16998c2ecf20Sopenharmony_ci	data->dma_addr = dma_addr;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	/*
17028c2ecf20Sopenharmony_ci	 * dma_alloc_coherent() uses a page allocator, so the address is
17038c2ecf20Sopenharmony_ci	 * always page-aligned.  We need the memory to be 32-byte aligned,
17048c2ecf20Sopenharmony_ci	 * so that's good.  However, if one day the allocator changes, we
17058c2ecf20Sopenharmony_ci	 * need to catch that.  It's not worth the effort to handle unaligned
17068c2ecf20Sopenharmony_ci	 * alloctions now because it's highly unlikely to ever be a problem.
17078c2ecf20Sopenharmony_ci	 */
17088c2ecf20Sopenharmony_ci	if ((unsigned long)data & 31) {
17098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "misaligned allocation");
17108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
17118c2ecf20Sopenharmony_ci		goto error;
17128c2ecf20Sopenharmony_ci	}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	spin_lock_init(&data->reg_lock);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_AOIS; i++) {
17178c2ecf20Sopenharmony_ci		struct fb_info *info = &data->fsl_diu_info[i];
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci		info->device = &pdev->dev;
17208c2ecf20Sopenharmony_ci		info->par = &data->mfb[i];
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci		/*
17238c2ecf20Sopenharmony_ci		 * We store the physical address of the AD in the reserved
17248c2ecf20Sopenharmony_ci		 * 'paddr' field of the AD itself.
17258c2ecf20Sopenharmony_ci		 */
17268c2ecf20Sopenharmony_ci		data->ad[i].paddr = DMA_ADDR(data, ad[i]);
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci		info->fix.smem_start = 0;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci		/* Initialize the AOI data structure */
17318c2ecf20Sopenharmony_ci		mfbi = info->par;
17328c2ecf20Sopenharmony_ci		memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
17338c2ecf20Sopenharmony_ci		mfbi->parent = data;
17348c2ecf20Sopenharmony_ci		mfbi->ad = &data->ad[i];
17358c2ecf20Sopenharmony_ci	}
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	/* Get the EDID data from the device tree, if present */
17388c2ecf20Sopenharmony_ci	prop = of_get_property(np, "edid", &ret);
17398c2ecf20Sopenharmony_ci	if (prop && ret == EDID_LENGTH) {
17408c2ecf20Sopenharmony_ci		memcpy(data->edid_data, prop, EDID_LENGTH);
17418c2ecf20Sopenharmony_ci		data->has_edid = true;
17428c2ecf20Sopenharmony_ci	}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	data->diu_reg = of_iomap(np, 0);
17458c2ecf20Sopenharmony_ci	if (!data->diu_reg) {
17468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot map DIU registers\n");
17478c2ecf20Sopenharmony_ci		ret = -EFAULT;
17488c2ecf20Sopenharmony_ci		goto error;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	/* Get the IRQ of the DIU */
17528c2ecf20Sopenharmony_ci	data->irq = irq_of_parse_and_map(np, 0);
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	if (!data->irq) {
17558c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not get DIU IRQ\n");
17568c2ecf20Sopenharmony_ci		ret = -EINVAL;
17578c2ecf20Sopenharmony_ci		goto error;
17588c2ecf20Sopenharmony_ci	}
17598c2ecf20Sopenharmony_ci	data->monitor_port = monitor_port;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	/* Initialize the dummy Area Descriptor */
17628c2ecf20Sopenharmony_ci	data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));
17638c2ecf20Sopenharmony_ci	data->dummy_ad.pix_fmt = 0x88882317;
17648c2ecf20Sopenharmony_ci	data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
17658c2ecf20Sopenharmony_ci	data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);
17668c2ecf20Sopenharmony_ci	data->dummy_ad.offset_xyi = 0;
17678c2ecf20Sopenharmony_ci	data->dummy_ad.offset_xyd = 0;
17688c2ecf20Sopenharmony_ci	data->dummy_ad.next_ad = 0;
17698c2ecf20Sopenharmony_ci	data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	/*
17728c2ecf20Sopenharmony_ci	 * Let DIU continue to display splash screen if it was pre-initialized
17738c2ecf20Sopenharmony_ci	 * by the bootloader; otherwise, clear the display.
17748c2ecf20Sopenharmony_ci	 */
17758c2ecf20Sopenharmony_ci	if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)
17768c2ecf20Sopenharmony_ci		out_be32(&data->diu_reg->desc[0], 0);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
17798c2ecf20Sopenharmony_ci	out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	/*
17828c2ecf20Sopenharmony_ci	 * Older versions of U-Boot leave interrupts enabled, so disable
17838c2ecf20Sopenharmony_ci	 * all of them and clear the status register.
17848c2ecf20Sopenharmony_ci	 */
17858c2ecf20Sopenharmony_ci	out_be32(&data->diu_reg->int_mask, 0xffffffff);
17868c2ecf20Sopenharmony_ci	in_be32(&data->diu_reg->int_status);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",
17898c2ecf20Sopenharmony_ci			  data->diu_reg);
17908c2ecf20Sopenharmony_ci	if (ret) {
17918c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not claim irq\n");
17928c2ecf20Sopenharmony_ci		goto error;
17938c2ecf20Sopenharmony_ci	}
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_AOIS; i++) {
17968c2ecf20Sopenharmony_ci		ret = install_fb(&data->fsl_diu_info[i]);
17978c2ecf20Sopenharmony_ci		if (ret) {
17988c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "could not register fb %d\n", i);
17998c2ecf20Sopenharmony_ci			free_irq(data->irq, data->diu_reg);
18008c2ecf20Sopenharmony_ci			goto error;
18018c2ecf20Sopenharmony_ci		}
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	sysfs_attr_init(&data->dev_attr.attr);
18058c2ecf20Sopenharmony_ci	data->dev_attr.attr.name = "monitor";
18068c2ecf20Sopenharmony_ci	data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
18078c2ecf20Sopenharmony_ci	data->dev_attr.show = show_monitor;
18088c2ecf20Sopenharmony_ci	data->dev_attr.store = store_monitor;
18098c2ecf20Sopenharmony_ci	ret = device_create_file(&pdev->dev, &data->dev_attr);
18108c2ecf20Sopenharmony_ci	if (ret) {
18118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "could not create sysfs file %s\n",
18128c2ecf20Sopenharmony_ci			data->dev_attr.attr.name);
18138c2ecf20Sopenharmony_ci	}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, data);
18168c2ecf20Sopenharmony_ci	return 0;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_cierror:
18198c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_AOIS; i++)
18208c2ecf20Sopenharmony_ci		uninstall_fb(&data->fsl_diu_info[i]);
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	iounmap(data->diu_reg);
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	return ret;
18258c2ecf20Sopenharmony_ci}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_cistatic int fsl_diu_remove(struct platform_device *pdev)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	struct fsl_diu_data *data;
18308c2ecf20Sopenharmony_ci	int i;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	data = dev_get_drvdata(&pdev->dev);
18338c2ecf20Sopenharmony_ci	disable_lcdc(&data->fsl_diu_info[0]);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	free_irq(data->irq, data->diu_reg);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_AOIS; i++)
18388c2ecf20Sopenharmony_ci		uninstall_fb(&data->fsl_diu_info[i]);
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	iounmap(data->diu_reg);
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	return 0;
18438c2ecf20Sopenharmony_ci}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci#ifndef MODULE
18468c2ecf20Sopenharmony_cistatic int __init fsl_diu_setup(char *options)
18478c2ecf20Sopenharmony_ci{
18488c2ecf20Sopenharmony_ci	char *opt;
18498c2ecf20Sopenharmony_ci	unsigned long val;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	if (!options || !*options)
18528c2ecf20Sopenharmony_ci		return 0;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	while ((opt = strsep(&options, ",")) != NULL) {
18558c2ecf20Sopenharmony_ci		if (!*opt)
18568c2ecf20Sopenharmony_ci			continue;
18578c2ecf20Sopenharmony_ci		if (!strncmp(opt, "monitor=", 8)) {
18588c2ecf20Sopenharmony_ci			monitor_port = fsl_diu_name_to_port(opt + 8);
18598c2ecf20Sopenharmony_ci		} else if (!strncmp(opt, "bpp=", 4)) {
18608c2ecf20Sopenharmony_ci			if (!kstrtoul(opt + 4, 10, &val))
18618c2ecf20Sopenharmony_ci				default_bpp = val;
18628c2ecf20Sopenharmony_ci		} else
18638c2ecf20Sopenharmony_ci			fb_mode = opt;
18648c2ecf20Sopenharmony_ci	}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	return 0;
18678c2ecf20Sopenharmony_ci}
18688c2ecf20Sopenharmony_ci#endif
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_diu_match[] = {
18718c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_MPC512x
18728c2ecf20Sopenharmony_ci	{
18738c2ecf20Sopenharmony_ci		.compatible = "fsl,mpc5121-diu",
18748c2ecf20Sopenharmony_ci	},
18758c2ecf20Sopenharmony_ci#endif
18768c2ecf20Sopenharmony_ci	{
18778c2ecf20Sopenharmony_ci		.compatible = "fsl,diu",
18788c2ecf20Sopenharmony_ci	},
18798c2ecf20Sopenharmony_ci	{}
18808c2ecf20Sopenharmony_ci};
18818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_diu_match);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_cistatic struct platform_driver fsl_diu_driver = {
18848c2ecf20Sopenharmony_ci	.driver = {
18858c2ecf20Sopenharmony_ci		.name = "fsl-diu-fb",
18868c2ecf20Sopenharmony_ci		.of_match_table = fsl_diu_match,
18878c2ecf20Sopenharmony_ci	},
18888c2ecf20Sopenharmony_ci	.probe  	= fsl_diu_probe,
18898c2ecf20Sopenharmony_ci	.remove 	= fsl_diu_remove,
18908c2ecf20Sopenharmony_ci	.suspend	= fsl_diu_suspend,
18918c2ecf20Sopenharmony_ci	.resume		= fsl_diu_resume,
18928c2ecf20Sopenharmony_ci};
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic int __init fsl_diu_init(void)
18958c2ecf20Sopenharmony_ci{
18968c2ecf20Sopenharmony_ci#ifdef CONFIG_NOT_COHERENT_CACHE
18978c2ecf20Sopenharmony_ci	struct device_node *np;
18988c2ecf20Sopenharmony_ci	const u32 *prop;
18998c2ecf20Sopenharmony_ci#endif
19008c2ecf20Sopenharmony_ci	int ret;
19018c2ecf20Sopenharmony_ci#ifndef MODULE
19028c2ecf20Sopenharmony_ci	char *option;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	/*
19058c2ecf20Sopenharmony_ci	 * For kernel boot options (in 'video=xxxfb:<options>' format)
19068c2ecf20Sopenharmony_ci	 */
19078c2ecf20Sopenharmony_ci	if (fb_get_options("fslfb", &option))
19088c2ecf20Sopenharmony_ci		return -ENODEV;
19098c2ecf20Sopenharmony_ci	fsl_diu_setup(option);
19108c2ecf20Sopenharmony_ci#else
19118c2ecf20Sopenharmony_ci	monitor_port = fsl_diu_name_to_port(monitor_string);
19128c2ecf20Sopenharmony_ci#endif
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	/*
19158c2ecf20Sopenharmony_ci	 * Must to verify set_pixel_clock. If not implement on platform,
19168c2ecf20Sopenharmony_ci	 * then that means that there is no platform support for the DIU.
19178c2ecf20Sopenharmony_ci	 */
19188c2ecf20Sopenharmony_ci	if (!diu_ops.set_pixel_clock)
19198c2ecf20Sopenharmony_ci		return -ENODEV;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci#ifdef CONFIG_NOT_COHERENT_CACHE
19248c2ecf20Sopenharmony_ci	np = of_get_cpu_node(0, NULL);
19258c2ecf20Sopenharmony_ci	if (!np) {
19268c2ecf20Sopenharmony_ci		pr_err("fsl-diu-fb: can't find 'cpu' device node\n");
19278c2ecf20Sopenharmony_ci		return -ENODEV;
19288c2ecf20Sopenharmony_ci	}
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	prop = of_get_property(np, "d-cache-size", NULL);
19318c2ecf20Sopenharmony_ci	if (prop == NULL) {
19328c2ecf20Sopenharmony_ci		pr_err("fsl-diu-fb: missing 'd-cache-size' property' "
19338c2ecf20Sopenharmony_ci		       "in 'cpu' node\n");
19348c2ecf20Sopenharmony_ci		of_node_put(np);
19358c2ecf20Sopenharmony_ci		return -ENODEV;
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	/*
19398c2ecf20Sopenharmony_ci	 * Freescale PLRU requires 13/8 times the cache size to do a proper
19408c2ecf20Sopenharmony_ci	 * displacement flush
19418c2ecf20Sopenharmony_ci	 */
19428c2ecf20Sopenharmony_ci	coherence_data_size = be32_to_cpup(prop) * 13;
19438c2ecf20Sopenharmony_ci	coherence_data_size /= 8;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",
19468c2ecf20Sopenharmony_ci		 coherence_data_size);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	prop = of_get_property(np, "d-cache-line-size", NULL);
19498c2ecf20Sopenharmony_ci	if (prop == NULL) {
19508c2ecf20Sopenharmony_ci		pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
19518c2ecf20Sopenharmony_ci		       "in 'cpu' node\n");
19528c2ecf20Sopenharmony_ci		of_node_put(np);
19538c2ecf20Sopenharmony_ci		return -ENODEV;
19548c2ecf20Sopenharmony_ci	}
19558c2ecf20Sopenharmony_ci	d_cache_line_size = be32_to_cpup(prop);
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",
19588c2ecf20Sopenharmony_ci		 d_cache_line_size);
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	of_node_put(np);
19618c2ecf20Sopenharmony_ci	coherence_data = vmalloc(coherence_data_size);
19628c2ecf20Sopenharmony_ci	if (!coherence_data)
19638c2ecf20Sopenharmony_ci		return -ENOMEM;
19648c2ecf20Sopenharmony_ci#endif
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	ret = platform_driver_register(&fsl_diu_driver);
19678c2ecf20Sopenharmony_ci	if (ret) {
19688c2ecf20Sopenharmony_ci		pr_err("fsl-diu-fb: failed to register platform driver\n");
19698c2ecf20Sopenharmony_ci#if defined(CONFIG_NOT_COHERENT_CACHE)
19708c2ecf20Sopenharmony_ci		vfree(coherence_data);
19718c2ecf20Sopenharmony_ci#endif
19728c2ecf20Sopenharmony_ci	}
19738c2ecf20Sopenharmony_ci	return ret;
19748c2ecf20Sopenharmony_ci}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_cistatic void __exit fsl_diu_exit(void)
19778c2ecf20Sopenharmony_ci{
19788c2ecf20Sopenharmony_ci	platform_driver_unregister(&fsl_diu_driver);
19798c2ecf20Sopenharmony_ci#if defined(CONFIG_NOT_COHERENT_CACHE)
19808c2ecf20Sopenharmony_ci	vfree(coherence_data);
19818c2ecf20Sopenharmony_ci#endif
19828c2ecf20Sopenharmony_ci}
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_cimodule_init(fsl_diu_init);
19858c2ecf20Sopenharmony_cimodule_exit(fsl_diu_exit);
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ciMODULE_AUTHOR("York Sun <yorksun@freescale.com>");
19888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale DIU framebuffer driver");
19898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cimodule_param_named(mode, fb_mode, charp, 0);
19928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode,
19938c2ecf20Sopenharmony_ci	"Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
19948c2ecf20Sopenharmony_cimodule_param_named(bpp, default_bpp, ulong, 0);
19958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");
19968c2ecf20Sopenharmony_cimodule_param_named(monitor, monitor_string, charp, 0);
19978c2ecf20Sopenharmony_ciMODULE_PARM_DESC(monitor, "Specify the monitor port "
19988c2ecf20Sopenharmony_ci	"(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");
19998c2ecf20Sopenharmony_ci
2000