11c06552aSJason Jin /*
21c06552aSJason Jin  * T1042 platform DIU operation
31c06552aSJason Jin  *
41c06552aSJason Jin  * Copyright 2014 Freescale Semiconductor Inc.
51c06552aSJason Jin  *
61c06552aSJason Jin  * This program is free software; you can redistribute it and/or modify it
71c06552aSJason Jin  * under the terms of the GNU General Public License as published by the
81c06552aSJason Jin  * Free Software Foundation; either version 2 of the License, or (at your
91c06552aSJason Jin  * option) any later version.
101c06552aSJason Jin  */
111c06552aSJason Jin 
12f5daf77aSRandy Dunlap #include <linux/init.h>
131c06552aSJason Jin #include <linux/io.h>
141c06552aSJason Jin #include <linux/kernel.h>
15f5daf77aSRandy Dunlap #include <linux/module.h>
161c06552aSJason Jin #include <linux/of.h>
171c06552aSJason Jin #include <linux/of_address.h>
181c06552aSJason Jin 
191c06552aSJason Jin #include <sysdev/fsl_soc.h>
201c06552aSJason Jin 
211c06552aSJason Jin /*DIU Pixel ClockCR offset in scfg*/
221c06552aSJason Jin #define CCSR_SCFG_PIXCLKCR      0x28
231c06552aSJason Jin 
241c06552aSJason Jin /* DIU Pixel Clock bits of the PIXCLKCR */
251c06552aSJason Jin #define PIXCLKCR_PXCKEN		0x80000000
261c06552aSJason Jin #define PIXCLKCR_PXCKINV	0x40000000
271c06552aSJason Jin #define PIXCLKCR_PXCKDLY	0x0000FF00
281c06552aSJason Jin #define PIXCLKCR_PXCLK_MASK	0x00FF0000
291c06552aSJason Jin 
301c06552aSJason Jin /* Some CPLD register definitions */
311c06552aSJason Jin #define CPLD_DIUCSR		0x16
321c06552aSJason Jin #define CPLD_DIUCSR_DVIEN	0x80
331c06552aSJason Jin #define CPLD_DIUCSR_BACKLIGHT	0x0f
341c06552aSJason Jin 
351c06552aSJason Jin struct device_node *cpld_node;
361c06552aSJason Jin 
371c06552aSJason Jin /**
381c06552aSJason Jin  * t1042rdb_set_monitor_port: switch the output to a different monitor port
391c06552aSJason Jin  */
401c06552aSJason Jin static void t1042rdb_set_monitor_port(enum fsl_diu_monitor_port port)
411c06552aSJason Jin {
42af8511cfSYue Haibing 	void __iomem *cpld_base;
431c06552aSJason Jin 
441c06552aSJason Jin 	cpld_base = of_iomap(cpld_node, 0);
451c06552aSJason Jin 	if (!cpld_base) {
461c06552aSJason Jin 		pr_err("%s: Could not map cpld registers\n", __func__);
471c06552aSJason Jin 		goto exit;
481c06552aSJason Jin 	}
491c06552aSJason Jin 
501c06552aSJason Jin 	switch (port) {
511c06552aSJason Jin 	case FSL_DIU_PORT_DVI:
521c06552aSJason Jin 		/* Enable the DVI(HDMI) port, disable the DFP and
531c06552aSJason Jin 		 * the backlight
541c06552aSJason Jin 		 */
551c06552aSJason Jin 		clrbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_DVIEN);
561c06552aSJason Jin 		break;
571c06552aSJason Jin 	case FSL_DIU_PORT_LVDS:
581c06552aSJason Jin 		/*
591c06552aSJason Jin 		 * LVDS also needs backlight enabled, otherwise the display
601c06552aSJason Jin 		 * will be blank.
611c06552aSJason Jin 		 */
621c06552aSJason Jin 		/* Enable the DFP port, disable the DVI*/
631c06552aSJason Jin 		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 8);
641c06552aSJason Jin 		setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 4);
651c06552aSJason Jin 		setbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_BACKLIGHT);
661c06552aSJason Jin 		break;
671c06552aSJason Jin 	default:
681c06552aSJason Jin 		pr_err("%s: Unsupported monitor port %i\n", __func__, port);
691c06552aSJason Jin 	}
701c06552aSJason Jin 
711c06552aSJason Jin 	iounmap(cpld_base);
721c06552aSJason Jin exit:
731c06552aSJason Jin 	of_node_put(cpld_node);
741c06552aSJason Jin }
751c06552aSJason Jin 
761c06552aSJason Jin /**
771c06552aSJason Jin  * t1042rdb_set_pixel_clock: program the DIU's clock
781c06552aSJason Jin  * @pixclock: pixel clock in ps (pico seconds)
791c06552aSJason Jin  */
801c06552aSJason Jin static void t1042rdb_set_pixel_clock(unsigned int pixclock)
811c06552aSJason Jin {
821c06552aSJason Jin 	struct device_node *scfg_np;
831c06552aSJason Jin 	void __iomem *scfg;
841c06552aSJason Jin 	unsigned long freq;
851c06552aSJason Jin 	u64 temp;
861c06552aSJason Jin 	u32 pxclk;
871c06552aSJason Jin 
881c06552aSJason Jin 	scfg_np = of_find_compatible_node(NULL, NULL, "fsl,t1040-scfg");
891c06552aSJason Jin 	if (!scfg_np) {
901c06552aSJason Jin 		pr_err("%s: Missing scfg node. Can not display video.\n",
911c06552aSJason Jin 		       __func__);
921c06552aSJason Jin 		return;
931c06552aSJason Jin 	}
941c06552aSJason Jin 
951c06552aSJason Jin 	scfg = of_iomap(scfg_np, 0);
961c06552aSJason Jin 	of_node_put(scfg_np);
971c06552aSJason Jin 	if (!scfg) {
981c06552aSJason Jin 		pr_err("%s: Could not map device. Can not display video.\n",
991c06552aSJason Jin 		       __func__);
1001c06552aSJason Jin 		return;
1011c06552aSJason Jin 	}
1021c06552aSJason Jin 
1031c06552aSJason Jin 	/* Convert pixclock into frequency */
1041c06552aSJason Jin 	temp = 1000000000000ULL;
1051c06552aSJason Jin 	do_div(temp, pixclock);
1061c06552aSJason Jin 	freq = temp;
1071c06552aSJason Jin 
1081c06552aSJason Jin 	/*
1091c06552aSJason Jin 	 * 'pxclk' is the ratio of the platform clock to the pixel clock.
1101c06552aSJason Jin 	 * This number is programmed into the PIXCLKCR register, and the valid
1111c06552aSJason Jin 	 * range of values is 2-255.
1121c06552aSJason Jin 	 */
1131c06552aSJason Jin 	pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq);
1141c06552aSJason Jin 	pxclk = clamp_t(u32, pxclk, 2, 255);
1151c06552aSJason Jin 
1161c06552aSJason Jin 	/* Disable the pixel clock, and set it to non-inverted and no delay */
1171c06552aSJason Jin 	clrbits32(scfg + CCSR_SCFG_PIXCLKCR,
1181c06552aSJason Jin 		  PIXCLKCR_PXCKEN | PIXCLKCR_PXCKDLY | PIXCLKCR_PXCLK_MASK);
1191c06552aSJason Jin 
1201c06552aSJason Jin 	/* Enable the clock and set the pxclk */
1211c06552aSJason Jin 	setbits32(scfg + CCSR_SCFG_PIXCLKCR, PIXCLKCR_PXCKEN | (pxclk << 16));
1221c06552aSJason Jin 
1231c06552aSJason Jin 	iounmap(scfg);
1241c06552aSJason Jin }
1251c06552aSJason Jin 
1261c06552aSJason Jin /**
1271c06552aSJason Jin  * t1042rdb_valid_monitor_port: set the monitor port for sysfs
1281c06552aSJason Jin  */
1291c06552aSJason Jin static enum fsl_diu_monitor_port
1301c06552aSJason Jin t1042rdb_valid_monitor_port(enum fsl_diu_monitor_port port)
1311c06552aSJason Jin {
1321c06552aSJason Jin 	switch (port) {
1331c06552aSJason Jin 	case FSL_DIU_PORT_DVI:
1341c06552aSJason Jin 	case FSL_DIU_PORT_LVDS:
1351c06552aSJason Jin 		return port;
1361c06552aSJason Jin 	default:
1371c06552aSJason Jin 		return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */
1381c06552aSJason Jin 	}
1391c06552aSJason Jin }
1401c06552aSJason Jin 
1411c06552aSJason Jin static int __init t1042rdb_diu_init(void)
1421c06552aSJason Jin {
1431c06552aSJason Jin 	cpld_node = of_find_compatible_node(NULL, NULL, "fsl,t1042rdb-cpld");
1441c06552aSJason Jin 	if (!cpld_node)
1451c06552aSJason Jin 		return 0;
1461c06552aSJason Jin 
1471c06552aSJason Jin 	diu_ops.set_monitor_port	= t1042rdb_set_monitor_port;
1481c06552aSJason Jin 	diu_ops.set_pixel_clock		= t1042rdb_set_pixel_clock;
1491c06552aSJason Jin 	diu_ops.valid_monitor_port	= t1042rdb_valid_monitor_port;
1501c06552aSJason Jin 
1511c06552aSJason Jin 	return 0;
1521c06552aSJason Jin }
1531c06552aSJason Jin 
1541c06552aSJason Jin early_initcall(t1042rdb_diu_init);
155f5daf77aSRandy Dunlap 
156f5daf77aSRandy Dunlap MODULE_LICENSE("GPL");
157