mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-31 16:18:51 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										142
									
								
								drivers/video/fbdev/omap2/dss/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								drivers/video/fbdev/omap2/dss/Kconfig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,142 @@ | |||
| config OMAP2_DSS_INIT | ||||
| 	bool | ||||
| 
 | ||||
| menuconfig OMAP2_DSS | ||||
|         tristate "OMAP2+ Display Subsystem support" | ||||
| 	select VIDEOMODE_HELPERS | ||||
| 	select OMAP2_DSS_INIT | ||||
| 	select HDMI | ||||
|         help | ||||
| 	  OMAP2+ Display Subsystem support. | ||||
| 
 | ||||
| if OMAP2_DSS | ||||
| 
 | ||||
| config OMAP2_DSS_DEBUG | ||||
| 	bool "Debug support" | ||||
| 	default n | ||||
| 	help | ||||
| 	  This enables printing of debug messages. Alternatively, debug messages | ||||
| 	  can also be enabled by setting CONFIG_DYNAMIC_DEBUG and then setting | ||||
| 	  appropriate flags in <debugfs>/dynamic_debug/control. | ||||
| 
 | ||||
| config OMAP2_DSS_DEBUGFS | ||||
| 	bool "Debugfs filesystem support" | ||||
| 	depends on DEBUG_FS | ||||
| 	default n | ||||
| 	help | ||||
| 	  This enables debugfs for OMAPDSS at <debugfs>/omapdss. This enables | ||||
| 	  querying about clock configuration and register configuration of dss, | ||||
| 	  dispc, dsi, hdmi and rfbi. | ||||
| 
 | ||||
| config OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| 	bool "Collect DSS IRQ statistics" | ||||
| 	depends on OMAP2_DSS_DEBUGFS | ||||
| 	default n | ||||
| 	help | ||||
| 	  Collect DSS IRQ statistics, printable via debugfs. | ||||
| 
 | ||||
| 	  The statistics can be found from | ||||
| 	  <debugfs>/omapdss/dispc_irq for DISPC interrupts, and | ||||
| 	  <debugfs>/omapdss/dsi_irq for DSI interrupts. | ||||
| 
 | ||||
| config OMAP2_DSS_DPI | ||||
| 	bool "DPI support" | ||||
| 	default y | ||||
| 	help | ||||
| 	  DPI Interface. This is the Parallel Display Interface. | ||||
| 
 | ||||
| config OMAP2_DSS_RFBI | ||||
| 	bool "RFBI support" | ||||
| 	depends on BROKEN | ||||
|         default n | ||||
| 	help | ||||
| 	  MIPI DBI support (RFBI, Remote Framebuffer Interface, in Texas | ||||
| 	  Instrument's terminology). | ||||
| 
 | ||||
| 	  DBI is a bus between the host processor and a peripheral, | ||||
| 	  such as a display or a framebuffer chip. | ||||
| 
 | ||||
| 	  See http://www.mipi.org/ for DBI specifications. | ||||
| 
 | ||||
| config OMAP2_DSS_VENC | ||||
| 	bool "VENC support" | ||||
|         default y | ||||
| 	help | ||||
| 	  OMAP Video Encoder support for S-Video and composite TV-out. | ||||
| 
 | ||||
| config OMAP2_DSS_HDMI_COMMON | ||||
| 	bool | ||||
| 
 | ||||
| config OMAP4_DSS_HDMI | ||||
| 	bool "HDMI support for OMAP4" | ||||
|         default y | ||||
| 	select OMAP2_DSS_HDMI_COMMON | ||||
| 	help | ||||
| 	  HDMI support for OMAP4 based SoCs. | ||||
| 
 | ||||
| config OMAP4_DSS_HDMI_AUDIO | ||||
| 	bool | ||||
| 
 | ||||
| config OMAP5_DSS_HDMI | ||||
| 	bool "HDMI support for OMAP5" | ||||
| 	default n | ||||
| 	select OMAP2_DSS_HDMI_COMMON | ||||
| 	help | ||||
| 	  HDMI Interface for OMAP5 and similar cores. This adds the High | ||||
| 	  Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI | ||||
| 	  specification. | ||||
| 
 | ||||
| config OMAP5_DSS_HDMI_AUDIO | ||||
| 	depends on OMAP5_DSS_HDMI | ||||
| 	bool | ||||
| 
 | ||||
| config OMAP2_DSS_SDI | ||||
| 	bool "SDI support" | ||||
|         default n | ||||
| 	help | ||||
| 	  SDI (Serial Display Interface) support. | ||||
| 
 | ||||
| 	  SDI is a high speed one-way display serial bus between the host | ||||
| 	  processor and a display. | ||||
| 
 | ||||
| config OMAP2_DSS_DSI | ||||
| 	bool "DSI support" | ||||
|         default n | ||||
| 	help | ||||
| 	  MIPI DSI (Display Serial Interface) support. | ||||
| 
 | ||||
| 	  DSI is a high speed half-duplex serial interface between the host | ||||
| 	  processor and a peripheral, such as a display or a framebuffer chip. | ||||
| 
 | ||||
| 	  See http://www.mipi.org/ for DSI specifications. | ||||
| 
 | ||||
| config OMAP2_DSS_MIN_FCK_PER_PCK | ||||
| 	int "Minimum FCK/PCK ratio (for scaling)" | ||||
| 	range 0 32 | ||||
| 	default 0 | ||||
| 	help | ||||
| 	  This can be used to adjust the minimum FCK/PCK ratio. | ||||
| 
 | ||||
| 	  With this you can make sure that DISPC FCK is at least | ||||
| 	  n x PCK. Video plane scaling requires higher FCK than | ||||
| 	  normally. | ||||
| 
 | ||||
| 	  If this is set to 0, there's no extra constraint on the | ||||
| 	  DISPC FCK. However, the FCK will at minimum be | ||||
| 	  2xPCK (if active matrix) or 3xPCK (if passive matrix). | ||||
| 
 | ||||
| 	  Max FCK is 173MHz, so this doesn't work if your PCK | ||||
| 	  is very high. | ||||
| 
 | ||||
| config OMAP2_DSS_SLEEP_AFTER_VENC_RESET | ||||
| 	bool "Sleep 20ms after VENC reset" | ||||
| 	default y | ||||
| 	help | ||||
| 	  There is a 20ms sleep after VENC reset which seemed to fix the | ||||
| 	  reset. The reason for the bug is unclear, and it's also unclear | ||||
| 	  on what platforms this happens. | ||||
| 
 | ||||
| 	  This option enables the sleep, and is enabled by default. You can | ||||
| 	  disable the sleep if it doesn't cause problems on your platform. | ||||
| 
 | ||||
| endif | ||||
							
								
								
									
										18
									
								
								drivers/video/fbdev/omap2/dss/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								drivers/video/fbdev/omap2/dss/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o | ||||
| obj-$(CONFIG_OMAP2_DSS) += omapdss.o | ||||
| # Core DSS files
 | ||||
| omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
 | ||||
| 	output.o dss-of.o | ||||
| # DSS compat layer files
 | ||||
| omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
 | ||||
| 	dispc-compat.o display-sysfs.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o | ||||
| omapdss-$(CONFIG_OMAP2_DSS_HDMI_COMMON) += hdmi_common.o hdmi_wp.o hdmi_pll.o \
 | ||||
| 	hdmi_phy.o | ||||
| omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o | ||||
| omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o | ||||
| ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG | ||||
							
								
								
									
										1702
									
								
								drivers/video/fbdev/omap2/dss/apply.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1702
									
								
								drivers/video/fbdev/omap2/dss/apply.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										366
									
								
								drivers/video/fbdev/omap2/dss/core.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								drivers/video/fbdev/omap2/dss/core.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,366 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/core.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "CORE" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/seq_file.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <linux/suspend.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static struct { | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	const char *default_display_name; | ||||
| } core; | ||||
| 
 | ||||
| static char *def_disp_name; | ||||
| module_param_named(def_disp, def_disp_name, charp, 0); | ||||
| MODULE_PARM_DESC(def_disp, "default display name"); | ||||
| 
 | ||||
| static bool dss_initialized; | ||||
| 
 | ||||
| const char *omapdss_get_default_display_name(void) | ||||
| { | ||||
| 	return core.default_display_name; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_get_default_display_name); | ||||
| 
 | ||||
| enum omapdss_version omapdss_get_version(void) | ||||
| { | ||||
| 	struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; | ||||
| 	return pdata->version; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_get_version); | ||||
| 
 | ||||
| bool omapdss_is_initialized(void) | ||||
| { | ||||
| 	return dss_initialized; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_is_initialized); | ||||
| 
 | ||||
| struct platform_device *dss_get_core_pdev(void) | ||||
| { | ||||
| 	return core.pdev; | ||||
| } | ||||
| 
 | ||||
| int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) | ||||
| { | ||||
| 	struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; | ||||
| 
 | ||||
| 	if (!board_data->dsi_enable_pads) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return board_data->dsi_enable_pads(dsi_id, lane_mask); | ||||
| } | ||||
| 
 | ||||
| void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask) | ||||
| { | ||||
| 	struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; | ||||
| 
 | ||||
| 	if (!board_data->dsi_disable_pads) | ||||
| 		return; | ||||
| 
 | ||||
| 	return board_data->dsi_disable_pads(dsi_id, lane_mask); | ||||
| } | ||||
| 
 | ||||
| int dss_set_min_bus_tput(struct device *dev, unsigned long tput) | ||||
| { | ||||
| 	struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; | ||||
| 
 | ||||
| 	if (pdata->set_min_bus_tput) | ||||
| 		return pdata->set_min_bus_tput(dev, tput); | ||||
| 	else | ||||
| 		return 0; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP2_DSS_DEBUGFS) | ||||
| static int dss_debug_show(struct seq_file *s, void *unused) | ||||
| { | ||||
| 	void (*func)(struct seq_file *) = s->private; | ||||
| 	func(s); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_debug_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	return single_open(file, dss_debug_show, inode->i_private); | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations dss_debug_fops = { | ||||
| 	.open           = dss_debug_open, | ||||
| 	.read           = seq_read, | ||||
| 	.llseek         = seq_lseek, | ||||
| 	.release        = single_release, | ||||
| }; | ||||
| 
 | ||||
| static struct dentry *dss_debugfs_dir; | ||||
| 
 | ||||
| static int dss_initialize_debugfs(void) | ||||
| { | ||||
| 	dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); | ||||
| 	if (IS_ERR(dss_debugfs_dir)) { | ||||
| 		int err = PTR_ERR(dss_debugfs_dir); | ||||
| 		dss_debugfs_dir = NULL; | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, | ||||
| 			&dss_debug_dump_clocks, &dss_debug_fops); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dss_uninitialize_debugfs(void) | ||||
| { | ||||
| 	if (dss_debugfs_dir) | ||||
| 		debugfs_remove_recursive(dss_debugfs_dir); | ||||
| } | ||||
| 
 | ||||
| int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) | ||||
| { | ||||
| 	struct dentry *d; | ||||
| 
 | ||||
| 	d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, | ||||
| 			write, &dss_debug_fops); | ||||
| 
 | ||||
| 	return PTR_ERR_OR_ZERO(d); | ||||
| } | ||||
| #else /* CONFIG_OMAP2_DSS_DEBUGFS */ | ||||
| static inline int dss_initialize_debugfs(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static inline void dss_uninitialize_debugfs(void) | ||||
| { | ||||
| } | ||||
| int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif /* CONFIG_OMAP2_DSS_DEBUGFS */ | ||||
| 
 | ||||
| /* PLATFORM DEVICE */ | ||||
| static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d) | ||||
| { | ||||
| 	DSSDBG("pm notif %lu\n", v); | ||||
| 
 | ||||
| 	switch (v) { | ||||
| 	case PM_SUSPEND_PREPARE: | ||||
| 		DSSDBG("suspending displays\n"); | ||||
| 		return dss_suspend_all_devices(); | ||||
| 
 | ||||
| 	case PM_POST_SUSPEND: | ||||
| 		DSSDBG("resuming displays\n"); | ||||
| 		return dss_resume_all_devices(); | ||||
| 
 | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static struct notifier_block omap_dss_pm_notif_block = { | ||||
| 	.notifier_call = omap_dss_pm_notif, | ||||
| }; | ||||
| 
 | ||||
| static int __init omap_dss_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_board_info *pdata = pdev->dev.platform_data; | ||||
| 	int r; | ||||
| 
 | ||||
| 	core.pdev = pdev; | ||||
| 
 | ||||
| 	dss_features_init(omapdss_get_version()); | ||||
| 
 | ||||
| 	r = dss_initialize_debugfs(); | ||||
| 	if (r) | ||||
| 		goto err_debugfs; | ||||
| 
 | ||||
| 	if (def_disp_name) | ||||
| 		core.default_display_name = def_disp_name; | ||||
| 	else if (pdata->default_display_name) | ||||
| 		core.default_display_name = pdata->default_display_name; | ||||
| 	else if (pdata->default_device) | ||||
| 		core.default_display_name = pdata->default_device->name; | ||||
| 
 | ||||
| 	register_pm_notifier(&omap_dss_pm_notif_block); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_debugfs: | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int omap_dss_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	unregister_pm_notifier(&omap_dss_pm_notif_block); | ||||
| 
 | ||||
| 	dss_uninitialize_debugfs(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void omap_dss_shutdown(struct platform_device *pdev) | ||||
| { | ||||
| 	DSSDBG("shutdown\n"); | ||||
| 	dss_disable_all_devices(); | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver omap_dss_driver = { | ||||
| 	.remove         = omap_dss_remove, | ||||
| 	.shutdown	= omap_dss_shutdown, | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| /* INIT */ | ||||
| static int (*dss_output_drv_reg_funcs[])(void) __initdata = { | ||||
| #ifdef CONFIG_OMAP2_DSS_DSI | ||||
| 	dsi_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_DPI | ||||
| 	dpi_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_SDI | ||||
| 	sdi_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_RFBI | ||||
| 	rfbi_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_VENC | ||||
| 	venc_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP4_DSS_HDMI | ||||
| 	hdmi4_init_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP5_DSS_HDMI | ||||
| 	hdmi5_init_platform_driver, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = { | ||||
| #ifdef CONFIG_OMAP2_DSS_DSI | ||||
| 	dsi_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_DPI | ||||
| 	dpi_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_SDI | ||||
| 	sdi_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_RFBI | ||||
| 	rfbi_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP2_DSS_VENC | ||||
| 	venc_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP4_DSS_HDMI | ||||
| 	hdmi4_uninit_platform_driver, | ||||
| #endif | ||||
| #ifdef CONFIG_OMAP5_DSS_HDMI | ||||
| 	hdmi5_uninit_platform_driver, | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)]; | ||||
| 
 | ||||
| static int __init omap_dss_init(void) | ||||
| { | ||||
| 	int r; | ||||
| 	int i; | ||||
| 
 | ||||
| 	r = platform_driver_probe(&omap_dss_driver, omap_dss_probe); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dss_init_platform_driver(); | ||||
| 	if (r) { | ||||
| 		DSSERR("Failed to initialize DSS platform driver\n"); | ||||
| 		goto err_dss; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dispc_init_platform_driver(); | ||||
| 	if (r) { | ||||
| 		DSSERR("Failed to initialize dispc platform driver\n"); | ||||
| 		goto err_dispc; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * It's ok if the output-driver register fails. It happens, for example, | ||||
| 	 * when there is no output-device (e.g. SDI for OMAP4). | ||||
| 	 */ | ||||
| 	for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { | ||||
| 		r = dss_output_drv_reg_funcs[i](); | ||||
| 		if (r == 0) | ||||
| 			dss_output_drv_loaded[i] = true; | ||||
| 	} | ||||
| 
 | ||||
| 	dss_initialized = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_dispc: | ||||
| 	dss_uninit_platform_driver(); | ||||
| err_dss: | ||||
| 	platform_driver_unregister(&omap_dss_driver); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void __exit omap_dss_exit(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) { | ||||
| 		if (dss_output_drv_loaded[i]) | ||||
| 			dss_output_drv_unreg_funcs[i](); | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_uninit_platform_driver(); | ||||
| 	dss_uninit_platform_driver(); | ||||
| 
 | ||||
| 	platform_driver_unregister(&omap_dss_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(omap_dss_init); | ||||
| module_exit(omap_dss_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); | ||||
| MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| 
 | ||||
							
								
								
									
										667
									
								
								drivers/video/fbdev/omap2/dss/dispc-compat.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										667
									
								
								drivers/video/fbdev/omap2/dss/dispc-compat.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,667 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2012 Texas Instruments | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "APPLY" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/seq_file.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| #include "dispc-compat.h" | ||||
| 
 | ||||
| #define DISPC_IRQ_MASK_ERROR            (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ | ||||
| 					 DISPC_IRQ_OCP_ERR | \ | ||||
| 					 DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ | ||||
| 					 DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ | ||||
| 					 DISPC_IRQ_SYNC_LOST | \ | ||||
| 					 DISPC_IRQ_SYNC_LOST_DIGIT) | ||||
| 
 | ||||
| #define DISPC_MAX_NR_ISRS		8 | ||||
| 
 | ||||
| struct omap_dispc_isr_data { | ||||
| 	omap_dispc_isr_t	isr; | ||||
| 	void			*arg; | ||||
| 	u32			mask; | ||||
| }; | ||||
| 
 | ||||
| struct dispc_irq_stats { | ||||
| 	unsigned long last_reset; | ||||
| 	unsigned irq_count; | ||||
| 	unsigned irqs[32]; | ||||
| }; | ||||
| 
 | ||||
| static struct { | ||||
| 	spinlock_t irq_lock; | ||||
| 	u32 irq_error_mask; | ||||
| 	struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; | ||||
| 	u32 error_irqs; | ||||
| 	struct work_struct error_work; | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| 	spinlock_t irq_stats_lock; | ||||
| 	struct dispc_irq_stats irq_stats; | ||||
| #endif | ||||
| } dispc_compat; | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| static void dispc_dump_irqs(struct seq_file *s) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	struct dispc_irq_stats stats; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags); | ||||
| 
 | ||||
| 	stats = dispc_compat.irq_stats; | ||||
| 	memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats)); | ||||
| 	dispc_compat.irq_stats.last_reset = jiffies; | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags); | ||||
| 
 | ||||
| 	seq_printf(s, "period %u ms\n", | ||||
| 			jiffies_to_msecs(jiffies - stats.last_reset)); | ||||
| 
 | ||||
| 	seq_printf(s, "irqs %d\n", stats.irq_count); | ||||
| #define PIS(x) \ | ||||
| 	seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]); | ||||
| 
 | ||||
| 	PIS(FRAMEDONE); | ||||
| 	PIS(VSYNC); | ||||
| 	PIS(EVSYNC_EVEN); | ||||
| 	PIS(EVSYNC_ODD); | ||||
| 	PIS(ACBIAS_COUNT_STAT); | ||||
| 	PIS(PROG_LINE_NUM); | ||||
| 	PIS(GFX_FIFO_UNDERFLOW); | ||||
| 	PIS(GFX_END_WIN); | ||||
| 	PIS(PAL_GAMMA_MASK); | ||||
| 	PIS(OCP_ERR); | ||||
| 	PIS(VID1_FIFO_UNDERFLOW); | ||||
| 	PIS(VID1_END_WIN); | ||||
| 	PIS(VID2_FIFO_UNDERFLOW); | ||||
| 	PIS(VID2_END_WIN); | ||||
| 	if (dss_feat_get_num_ovls() > 3) { | ||||
| 		PIS(VID3_FIFO_UNDERFLOW); | ||||
| 		PIS(VID3_END_WIN); | ||||
| 	} | ||||
| 	PIS(SYNC_LOST); | ||||
| 	PIS(SYNC_LOST_DIGIT); | ||||
| 	PIS(WAKEUP); | ||||
| 	if (dss_has_feature(FEAT_MGR_LCD2)) { | ||||
| 		PIS(FRAMEDONE2); | ||||
| 		PIS(VSYNC2); | ||||
| 		PIS(ACBIAS_COUNT_STAT2); | ||||
| 		PIS(SYNC_LOST2); | ||||
| 	} | ||||
| 	if (dss_has_feature(FEAT_MGR_LCD3)) { | ||||
| 		PIS(FRAMEDONE3); | ||||
| 		PIS(VSYNC3); | ||||
| 		PIS(ACBIAS_COUNT_STAT3); | ||||
| 		PIS(SYNC_LOST3); | ||||
| 	} | ||||
| #undef PIS | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /* dispc.irq_lock has to be locked by the caller */ | ||||
| static void _omap_dispc_set_irqs(void) | ||||
| { | ||||
| 	u32 mask; | ||||
| 	int i; | ||||
| 	struct omap_dispc_isr_data *isr_data; | ||||
| 
 | ||||
| 	mask = dispc_compat.irq_error_mask; | ||||
| 
 | ||||
| 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { | ||||
| 		isr_data = &dispc_compat.registered_isr[i]; | ||||
| 
 | ||||
| 		if (isr_data->isr == NULL) | ||||
| 			continue; | ||||
| 
 | ||||
| 		mask |= isr_data->mask; | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_write_irqenable(mask); | ||||
| } | ||||
| 
 | ||||
| int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) | ||||
| { | ||||
| 	int i; | ||||
| 	int ret; | ||||
| 	unsigned long flags; | ||||
| 	struct omap_dispc_isr_data *isr_data; | ||||
| 
 | ||||
| 	if (isr == NULL) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	/* check for duplicate entry */ | ||||
| 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { | ||||
| 		isr_data = &dispc_compat.registered_isr[i]; | ||||
| 		if (isr_data->isr == isr && isr_data->arg == arg && | ||||
| 				isr_data->mask == mask) { | ||||
| 			ret = -EINVAL; | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	isr_data = NULL; | ||||
| 	ret = -EBUSY; | ||||
| 
 | ||||
| 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { | ||||
| 		isr_data = &dispc_compat.registered_isr[i]; | ||||
| 
 | ||||
| 		if (isr_data->isr != NULL) | ||||
| 			continue; | ||||
| 
 | ||||
| 		isr_data->isr = isr; | ||||
| 		isr_data->arg = arg; | ||||
| 		isr_data->mask = mask; | ||||
| 		ret = 0; | ||||
| 
 | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	_omap_dispc_set_irqs(); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dispc_register_isr); | ||||
| 
 | ||||
| int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) | ||||
| { | ||||
| 	int i; | ||||
| 	unsigned long flags; | ||||
| 	int ret = -EINVAL; | ||||
| 	struct omap_dispc_isr_data *isr_data; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { | ||||
| 		isr_data = &dispc_compat.registered_isr[i]; | ||||
| 		if (isr_data->isr != isr || isr_data->arg != arg || | ||||
| 				isr_data->mask != mask) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* found the correct isr */ | ||||
| 
 | ||||
| 		isr_data->isr = NULL; | ||||
| 		isr_data->arg = NULL; | ||||
| 		isr_data->mask = 0; | ||||
| 
 | ||||
| 		ret = 0; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret == 0) | ||||
| 		_omap_dispc_set_irqs(); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dispc_unregister_isr); | ||||
| 
 | ||||
| static void print_irq_status(u32 status) | ||||
| { | ||||
| 	if ((status & dispc_compat.irq_error_mask) == 0) | ||||
| 		return; | ||||
| 
 | ||||
| #define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : "" | ||||
| 
 | ||||
| 	pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n", | ||||
| 		status, | ||||
| 		PIS(OCP_ERR), | ||||
| 		PIS(GFX_FIFO_UNDERFLOW), | ||||
| 		PIS(VID1_FIFO_UNDERFLOW), | ||||
| 		PIS(VID2_FIFO_UNDERFLOW), | ||||
| 		dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "", | ||||
| 		PIS(SYNC_LOST), | ||||
| 		PIS(SYNC_LOST_DIGIT), | ||||
| 		dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "", | ||||
| 		dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : ""); | ||||
| #undef PIS | ||||
| } | ||||
| 
 | ||||
| /* Called from dss.c. Note that we don't touch clocks here,
 | ||||
|  * but we presume they are on because we got an IRQ. However, | ||||
|  * an irq handler may turn the clocks off, so we may not have | ||||
|  * clock later in the function. */ | ||||
| static irqreturn_t omap_dispc_irq_handler(int irq, void *arg) | ||||
| { | ||||
| 	int i; | ||||
| 	u32 irqstatus, irqenable; | ||||
| 	u32 handledirqs = 0; | ||||
| 	u32 unhandled_errors; | ||||
| 	struct omap_dispc_isr_data *isr_data; | ||||
| 	struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; | ||||
| 
 | ||||
| 	spin_lock(&dispc_compat.irq_lock); | ||||
| 
 | ||||
| 	irqstatus = dispc_read_irqstatus(); | ||||
| 	irqenable = dispc_read_irqenable(); | ||||
| 
 | ||||
| 	/* IRQ is not for us */ | ||||
| 	if (!(irqstatus & irqenable)) { | ||||
| 		spin_unlock(&dispc_compat.irq_lock); | ||||
| 		return IRQ_NONE; | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| 	spin_lock(&dispc_compat.irq_stats_lock); | ||||
| 	dispc_compat.irq_stats.irq_count++; | ||||
| 	dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs); | ||||
| 	spin_unlock(&dispc_compat.irq_stats_lock); | ||||
| #endif | ||||
| 
 | ||||
| 	print_irq_status(irqstatus); | ||||
| 
 | ||||
| 	/* Ack the interrupt. Do it here before clocks are possibly turned
 | ||||
| 	 * off */ | ||||
| 	dispc_clear_irqstatus(irqstatus); | ||||
| 	/* flush posted write */ | ||||
| 	dispc_read_irqstatus(); | ||||
| 
 | ||||
| 	/* make a copy and unlock, so that isrs can unregister
 | ||||
| 	 * themselves */ | ||||
| 	memcpy(registered_isr, dispc_compat.registered_isr, | ||||
| 			sizeof(registered_isr)); | ||||
| 
 | ||||
| 	spin_unlock(&dispc_compat.irq_lock); | ||||
| 
 | ||||
| 	for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { | ||||
| 		isr_data = ®istered_isr[i]; | ||||
| 
 | ||||
| 		if (!isr_data->isr) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (isr_data->mask & irqstatus) { | ||||
| 			isr_data->isr(isr_data->arg, irqstatus); | ||||
| 			handledirqs |= isr_data->mask; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock(&dispc_compat.irq_lock); | ||||
| 
 | ||||
| 	unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask; | ||||
| 
 | ||||
| 	if (unhandled_errors) { | ||||
| 		dispc_compat.error_irqs |= unhandled_errors; | ||||
| 
 | ||||
| 		dispc_compat.irq_error_mask &= ~unhandled_errors; | ||||
| 		_omap_dispc_set_irqs(); | ||||
| 
 | ||||
| 		schedule_work(&dispc_compat.error_work); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&dispc_compat.irq_lock); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static void dispc_error_worker(struct work_struct *work) | ||||
| { | ||||
| 	int i; | ||||
| 	u32 errors; | ||||
| 	unsigned long flags; | ||||
| 	static const unsigned fifo_underflow_bits[] = { | ||||
| 		DISPC_IRQ_GFX_FIFO_UNDERFLOW, | ||||
| 		DISPC_IRQ_VID1_FIFO_UNDERFLOW, | ||||
| 		DISPC_IRQ_VID2_FIFO_UNDERFLOW, | ||||
| 		DISPC_IRQ_VID3_FIFO_UNDERFLOW, | ||||
| 	}; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dispc_compat.irq_lock, flags); | ||||
| 	errors = dispc_compat.error_irqs; | ||||
| 	dispc_compat.error_irqs = 0; | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	dispc_runtime_get(); | ||||
| 
 | ||||
| 	for (i = 0; i < omap_dss_get_num_overlays(); ++i) { | ||||
| 		struct omap_overlay *ovl; | ||||
| 		unsigned bit; | ||||
| 
 | ||||
| 		ovl = omap_dss_get_overlay(i); | ||||
| 		bit = fifo_underflow_bits[i]; | ||||
| 
 | ||||
| 		if (bit & errors) { | ||||
| 			DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", | ||||
| 					ovl->name); | ||||
| 			ovl->disable(ovl); | ||||
| 			msleep(50); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { | ||||
| 		struct omap_overlay_manager *mgr; | ||||
| 		unsigned bit; | ||||
| 
 | ||||
| 		mgr = omap_dss_get_overlay_manager(i); | ||||
| 		bit = dispc_mgr_get_sync_lost_irq(i); | ||||
| 
 | ||||
| 		if (bit & errors) { | ||||
| 			int j; | ||||
| 
 | ||||
| 			DSSERR("SYNC_LOST on channel %s, restarting the output " | ||||
| 					"with video overlays disabled\n", | ||||
| 					mgr->name); | ||||
| 
 | ||||
| 			dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 			for (j = 0; j < omap_dss_get_num_overlays(); ++j) { | ||||
| 				struct omap_overlay *ovl; | ||||
| 				ovl = omap_dss_get_overlay(j); | ||||
| 
 | ||||
| 				if (ovl->id != OMAP_DSS_GFX && | ||||
| 						ovl->manager == mgr) | ||||
| 					ovl->disable(ovl); | ||||
| 			} | ||||
| 
 | ||||
| 			dss_mgr_enable(mgr); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (errors & DISPC_IRQ_OCP_ERR) { | ||||
| 		DSSERR("OCP_ERR\n"); | ||||
| 		for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { | ||||
| 			struct omap_overlay_manager *mgr; | ||||
| 
 | ||||
| 			mgr = omap_dss_get_overlay_manager(i); | ||||
| 			dss_mgr_disable(mgr); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dispc_compat.irq_lock, flags); | ||||
| 	dispc_compat.irq_error_mask |= errors; | ||||
| 	_omap_dispc_set_irqs(); | ||||
| 	spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| } | ||||
| 
 | ||||
| int dss_dispc_initialize_irq(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| 	spin_lock_init(&dispc_compat.irq_stats_lock); | ||||
| 	dispc_compat.irq_stats.last_reset = jiffies; | ||||
| 	dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); | ||||
| #endif | ||||
| 
 | ||||
| 	spin_lock_init(&dispc_compat.irq_lock); | ||||
| 
 | ||||
| 	memset(dispc_compat.registered_isr, 0, | ||||
| 			sizeof(dispc_compat.registered_isr)); | ||||
| 
 | ||||
| 	dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR; | ||||
| 	if (dss_has_feature(FEAT_MGR_LCD2)) | ||||
| 		dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; | ||||
| 	if (dss_has_feature(FEAT_MGR_LCD3)) | ||||
| 		dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; | ||||
| 	if (dss_feat_get_num_ovls() > 3) | ||||
| 		dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * there's SYNC_LOST_DIGIT waiting after enabling the DSS, | ||||
| 	 * so clear it | ||||
| 	 */ | ||||
| 	dispc_clear_irqstatus(dispc_read_irqstatus()); | ||||
| 
 | ||||
| 	INIT_WORK(&dispc_compat.error_work, dispc_error_worker); | ||||
| 
 | ||||
| 	_omap_dispc_set_irqs(); | ||||
| 
 | ||||
| 	r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat); | ||||
| 	if (r) { | ||||
| 		DSSERR("dispc_request_irq failed\n"); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dss_dispc_uninitialize_irq(void) | ||||
| { | ||||
| 	dispc_free_irq(&dispc_compat); | ||||
| } | ||||
| 
 | ||||
| static void dispc_mgr_disable_isr(void *data, u32 mask) | ||||
| { | ||||
| 	struct completion *compl = data; | ||||
| 	complete(compl); | ||||
| } | ||||
| 
 | ||||
| static void dispc_mgr_enable_lcd_out(enum omap_channel channel) | ||||
| { | ||||
| 	dispc_mgr_enable(channel, true); | ||||
| } | ||||
| 
 | ||||
| static void dispc_mgr_disable_lcd_out(enum omap_channel channel) | ||||
| { | ||||
| 	DECLARE_COMPLETION_ONSTACK(framedone_compl); | ||||
| 	int r; | ||||
| 	u32 irq; | ||||
| 
 | ||||
| 	if (dispc_mgr_is_enabled(channel) == false) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * When we disable LCD output, we need to wait for FRAMEDONE to know | ||||
| 	 * that DISPC has finished with the LCD output. | ||||
| 	 */ | ||||
| 
 | ||||
| 	irq = dispc_mgr_get_framedone_irq(channel); | ||||
| 
 | ||||
| 	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, | ||||
| 			irq); | ||||
| 	if (r) | ||||
| 		DSSERR("failed to register FRAMEDONE isr\n"); | ||||
| 
 | ||||
| 	dispc_mgr_enable(channel, false); | ||||
| 
 | ||||
| 	/* if we couldn't register for framedone, just sleep and exit */ | ||||
| 	if (r) { | ||||
| 		msleep(100); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wait_for_completion_timeout(&framedone_compl, | ||||
| 				msecs_to_jiffies(100))) | ||||
| 		DSSERR("timeout waiting for FRAME DONE\n"); | ||||
| 
 | ||||
| 	r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, | ||||
| 			irq); | ||||
| 	if (r) | ||||
| 		DSSERR("failed to unregister FRAMEDONE isr\n"); | ||||
| } | ||||
| 
 | ||||
| static void dispc_digit_out_enable_isr(void *data, u32 mask) | ||||
| { | ||||
| 	struct completion *compl = data; | ||||
| 
 | ||||
| 	/* ignore any sync lost interrupts */ | ||||
| 	if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD)) | ||||
| 		complete(compl); | ||||
| } | ||||
| 
 | ||||
| static void dispc_mgr_enable_digit_out(void) | ||||
| { | ||||
| 	DECLARE_COMPLETION_ONSTACK(vsync_compl); | ||||
| 	int r; | ||||
| 	u32 irq_mask; | ||||
| 
 | ||||
| 	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == true) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Digit output produces some sync lost interrupts during the first | ||||
| 	 * frame when enabling. Those need to be ignored, so we register for the | ||||
| 	 * sync lost irq to prevent the error handler from triggering. | ||||
| 	 */ | ||||
| 
 | ||||
| 	irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) | | ||||
| 		dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT); | ||||
| 
 | ||||
| 	r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl, | ||||
| 			irq_mask); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to register %x isr\n", irq_mask); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true); | ||||
| 
 | ||||
| 	/* wait for the first evsync */ | ||||
| 	if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100))) | ||||
| 		DSSERR("timeout waiting for digit out to start\n"); | ||||
| 
 | ||||
| 	r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl, | ||||
| 			irq_mask); | ||||
| 	if (r) | ||||
| 		DSSERR("failed to unregister %x isr\n", irq_mask); | ||||
| } | ||||
| 
 | ||||
| static void dispc_mgr_disable_digit_out(void) | ||||
| { | ||||
| 	DECLARE_COMPLETION_ONSTACK(framedone_compl); | ||||
| 	int r, i; | ||||
| 	u32 irq_mask; | ||||
| 	int num_irqs; | ||||
| 
 | ||||
| 	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) == false) | ||||
| 		return; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * When we disable the digit output, we need to wait for FRAMEDONE to | ||||
| 	 * know that DISPC has finished with the output. | ||||
| 	 */ | ||||
| 
 | ||||
| 	irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT); | ||||
| 	num_irqs = 1; | ||||
| 
 | ||||
| 	if (!irq_mask) { | ||||
| 		/*
 | ||||
| 		 * omap 2/3 don't have framedone irq for TV, so we need to use | ||||
| 		 * vsyncs for this. | ||||
| 		 */ | ||||
| 
 | ||||
| 		irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT); | ||||
| 		/*
 | ||||
| 		 * We need to wait for both even and odd vsyncs. Note that this | ||||
| 		 * is not totally reliable, as we could get a vsync interrupt | ||||
| 		 * before we disable the output, which leads to timeout in the | ||||
| 		 * wait_for_completion. | ||||
| 		 */ | ||||
| 		num_irqs = 2; | ||||
| 	} | ||||
| 
 | ||||
| 	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, | ||||
| 			irq_mask); | ||||
| 	if (r) | ||||
| 		DSSERR("failed to register %x isr\n", irq_mask); | ||||
| 
 | ||||
| 	dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false); | ||||
| 
 | ||||
| 	/* if we couldn't register the irq, just sleep and exit */ | ||||
| 	if (r) { | ||||
| 		msleep(100); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < num_irqs; ++i) { | ||||
| 		if (!wait_for_completion_timeout(&framedone_compl, | ||||
| 					msecs_to_jiffies(100))) | ||||
| 			DSSERR("timeout waiting for digit out to stop\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, | ||||
| 			irq_mask); | ||||
| 	if (r) | ||||
| 		DSSERR("failed to unregister %x isr\n", irq_mask); | ||||
| } | ||||
| 
 | ||||
| void dispc_mgr_enable_sync(enum omap_channel channel) | ||||
| { | ||||
| 	if (dss_mgr_is_lcd(channel)) | ||||
| 		dispc_mgr_enable_lcd_out(channel); | ||||
| 	else if (channel == OMAP_DSS_CHANNEL_DIGIT) | ||||
| 		dispc_mgr_enable_digit_out(); | ||||
| 	else | ||||
| 		WARN_ON(1); | ||||
| } | ||||
| 
 | ||||
| void dispc_mgr_disable_sync(enum omap_channel channel) | ||||
| { | ||||
| 	if (dss_mgr_is_lcd(channel)) | ||||
| 		dispc_mgr_disable_lcd_out(channel); | ||||
| 	else if (channel == OMAP_DSS_CHANNEL_DIGIT) | ||||
| 		dispc_mgr_disable_digit_out(); | ||||
| 	else | ||||
| 		WARN_ON(1); | ||||
| } | ||||
| 
 | ||||
| static inline void dispc_irq_wait_handler(void *data, u32 mask) | ||||
| { | ||||
| 	complete((struct completion *)data); | ||||
| } | ||||
| 
 | ||||
| int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, | ||||
| 		unsigned long timeout) | ||||
| { | ||||
| 
 | ||||
| 	int r; | ||||
| 	DECLARE_COMPLETION_ONSTACK(completion); | ||||
| 
 | ||||
| 	r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, | ||||
| 			irqmask); | ||||
| 
 | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	timeout = wait_for_completion_interruptible_timeout(&completion, | ||||
| 			timeout); | ||||
| 
 | ||||
| 	omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); | ||||
| 
 | ||||
| 	if (timeout == 0) | ||||
| 		return -ETIMEDOUT; | ||||
| 
 | ||||
| 	if (timeout == -ERESTARTSYS) | ||||
| 		return -ERESTARTSYS; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										30
									
								
								drivers/video/fbdev/omap2/dss/dispc-compat.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								drivers/video/fbdev/omap2/dss/dispc-compat.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2012 Texas Instruments | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __OMAP2_DSS_DISPC_COMPAT_H | ||||
| #define __OMAP2_DSS_DISPC_COMPAT_H | ||||
| 
 | ||||
| void dispc_mgr_enable_sync(enum omap_channel channel); | ||||
| void dispc_mgr_disable_sync(enum omap_channel channel); | ||||
| 
 | ||||
| int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, | ||||
| 		unsigned long timeout); | ||||
| 
 | ||||
| int dss_dispc_initialize_irq(void); | ||||
| void dss_dispc_uninitialize_irq(void); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										3857
									
								
								drivers/video/fbdev/omap2/dss/dispc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3857
									
								
								drivers/video/fbdev/omap2/dss/dispc.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										916
									
								
								drivers/video/fbdev/omap2/dss/dispc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										916
									
								
								drivers/video/fbdev/omap2/dss/dispc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,916 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dispc.h | ||||
|  * | ||||
|  * Copyright (C) 2011 Texas Instruments | ||||
|  * Author: Archit Taneja <archit@ti.com> | ||||
|  * | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __OMAP2_DISPC_REG_H | ||||
| #define __OMAP2_DISPC_REG_H | ||||
| 
 | ||||
| /* DISPC common registers */ | ||||
| #define DISPC_REVISION			0x0000 | ||||
| #define DISPC_SYSCONFIG			0x0010 | ||||
| #define DISPC_SYSSTATUS			0x0014 | ||||
| #define DISPC_IRQSTATUS			0x0018 | ||||
| #define DISPC_IRQENABLE			0x001C | ||||
| #define DISPC_CONTROL			0x0040 | ||||
| #define DISPC_CONFIG			0x0044 | ||||
| #define DISPC_CAPABLE			0x0048 | ||||
| #define DISPC_LINE_STATUS		0x005C | ||||
| #define DISPC_LINE_NUMBER		0x0060 | ||||
| #define DISPC_GLOBAL_ALPHA		0x0074 | ||||
| #define DISPC_CONTROL2			0x0238 | ||||
| #define DISPC_CONFIG2			0x0620 | ||||
| #define DISPC_DIVISOR			0x0804 | ||||
| #define DISPC_GLOBAL_BUFFER		0x0800 | ||||
| #define DISPC_CONTROL3                  0x0848 | ||||
| #define DISPC_CONFIG3                   0x084C | ||||
| #define DISPC_MSTANDBY_CTRL		0x0858 | ||||
| #define DISPC_GLOBAL_MFLAG_ATTRIBUTE	0x085C | ||||
| 
 | ||||
| /* DISPC overlay registers */ | ||||
| #define DISPC_OVL_BA0(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_BA0_OFFSET(n)) | ||||
| #define DISPC_OVL_BA1(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_BA1_OFFSET(n)) | ||||
| #define DISPC_OVL_BA0_UV(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_BA0_UV_OFFSET(n)) | ||||
| #define DISPC_OVL_BA1_UV(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_BA1_UV_OFFSET(n)) | ||||
| #define DISPC_OVL_POSITION(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_POS_OFFSET(n)) | ||||
| #define DISPC_OVL_SIZE(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_SIZE_OFFSET(n)) | ||||
| #define DISPC_OVL_ATTRIBUTES(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ATTR_OFFSET(n)) | ||||
| #define DISPC_OVL_ATTRIBUTES2(n)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ATTR2_OFFSET(n)) | ||||
| #define DISPC_OVL_FIFO_THRESHOLD(n)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIFO_THRESH_OFFSET(n)) | ||||
| #define DISPC_OVL_FIFO_SIZE_STATUS(n)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIFO_SIZE_STATUS_OFFSET(n)) | ||||
| #define DISPC_OVL_ROW_INC(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ROW_INC_OFFSET(n)) | ||||
| #define DISPC_OVL_PIXEL_INC(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_PIX_INC_OFFSET(n)) | ||||
| #define DISPC_OVL_WINDOW_SKIP(n)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_WINDOW_SKIP_OFFSET(n)) | ||||
| #define DISPC_OVL_TABLE_BA(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_TABLE_BA_OFFSET(n)) | ||||
| #define DISPC_OVL_FIR(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_OFFSET(n)) | ||||
| #define DISPC_OVL_FIR2(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR2_OFFSET(n)) | ||||
| #define DISPC_OVL_PICTURE_SIZE(n)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_PIC_SIZE_OFFSET(n)) | ||||
| #define DISPC_OVL_ACCU0(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ACCU0_OFFSET(n)) | ||||
| #define DISPC_OVL_ACCU1(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ACCU1_OFFSET(n)) | ||||
| #define DISPC_OVL_ACCU2_0(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ACCU2_0_OFFSET(n)) | ||||
| #define DISPC_OVL_ACCU2_1(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_ACCU2_1_OFFSET(n)) | ||||
| #define DISPC_OVL_FIR_COEF_H(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_H_OFFSET(n, i)) | ||||
| #define DISPC_OVL_FIR_COEF_HV(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_HV_OFFSET(n, i)) | ||||
| #define DISPC_OVL_FIR_COEF_H2(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_H2_OFFSET(n, i)) | ||||
| #define DISPC_OVL_FIR_COEF_HV2(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_HV2_OFFSET(n, i)) | ||||
| #define DISPC_OVL_CONV_COEF(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_CONV_COEF_OFFSET(n, i)) | ||||
| #define DISPC_OVL_FIR_COEF_V(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_V_OFFSET(n, i)) | ||||
| #define DISPC_OVL_FIR_COEF_V2(n, i)	(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_FIR_COEF_V2_OFFSET(n, i)) | ||||
| #define DISPC_OVL_PRELOAD(n)		(DISPC_OVL_BASE(n) + \ | ||||
| 					DISPC_PRELOAD_OFFSET(n)) | ||||
| #define DISPC_OVL_MFLAG_THRESHOLD(n)	DISPC_MFLAG_THRESHOLD_OFFSET(n) | ||||
| 
 | ||||
| /* DISPC up/downsampling FIR filter coefficient structure */ | ||||
| struct dispc_coef { | ||||
| 	s8 hc4_vc22; | ||||
| 	s8 hc3_vc2; | ||||
| 	u8 hc2_vc1; | ||||
| 	s8 hc1_vc0; | ||||
| 	s8 hc0_vc00; | ||||
| }; | ||||
| 
 | ||||
| const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps); | ||||
| 
 | ||||
| /* DISPC manager/channel specific registers */ | ||||
| static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x004C; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		return 0x0050; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03AC; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0814; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0054; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		return 0x0058; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03B0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0818; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_TIMING_H(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0064; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x0400; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0840; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_TIMING_V(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0068; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x0404; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0844; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_POL_FREQ(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x006C; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x0408; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x083C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_DIVISORo(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0070; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x040C; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0838; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */ | ||||
| static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x007C; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		return 0x0078; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03CC; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0834; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x01D4; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03C0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0828; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x01D8; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03C4; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x082C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x01DC; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03C8; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0830; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0220; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03BC; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0824; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0224; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03B8; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x0820; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return 0x0228; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return 0x03B4; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return 0x081C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* DISPC overlay register base addresses */ | ||||
| static inline u16 DISPC_OVL_BASE(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0080; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x00BC; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x014C; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0300; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0500; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* DISPC overlay register offsets */ | ||||
| static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0000; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0008; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0004; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x000C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0544; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x04BC; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0310; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0118; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0548; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x04C0; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0314; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x011C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0008; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x009C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x000C; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x00A8; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0020; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0010; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0070; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0568; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x04DC; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x032C; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0310; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0024; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0014; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x008C; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0028; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0018; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0088; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x002C; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x001C; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x00A4; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0030; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0020; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0098; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0034; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0038; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0024; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0090; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0580; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x055C; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0424; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x290; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0028; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0094; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x002C; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0000; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0584; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0560; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0428; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0294; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0030; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0004; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0588; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0564; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x042C; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0298; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0034 + i * 0x8; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0010 + i * 0x8; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x058C + i * 0x8; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0568 + i * 0x8; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0430 + i * 0x8; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x02A0 + i * 0x8; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0038 + i * 0x8; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0014 + i * 0x8; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0590 + i * 8; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x056C + i * 0x8; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0434 + i * 0x8; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x02A4 + i * 0x8; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4,} */ | ||||
| static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0074 + i * 0x4; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0124 + i * 0x4; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x00B4 + i * 0x4; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x0050 + i * 0x4; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ | ||||
| static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x05CC + i * 0x4; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x05A8 + i * 0x4; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x0470 + i * 0x4; | ||||
| 	case OMAP_DSS_WB: | ||||
| 		return 0x02E0 + i * 0x4; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x01AC; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0174; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x00E8; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x00A0; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline u16 DISPC_MFLAG_THRESHOLD_OFFSET(enum omap_plane plane) | ||||
| { | ||||
| 	switch (plane) { | ||||
| 	case OMAP_DSS_GFX: | ||||
| 		return 0x0860; | ||||
| 	case OMAP_DSS_VIDEO1: | ||||
| 		return 0x0864; | ||||
| 	case OMAP_DSS_VIDEO2: | ||||
| 		return 0x0868; | ||||
| 	case OMAP_DSS_VIDEO3: | ||||
| 		return 0x086c; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										325
									
								
								drivers/video/fbdev/omap2/dss/dispc_coefs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								drivers/video/fbdev/omap2/dss/dispc_coefs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,325 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dispc_coefs.c | ||||
|  * | ||||
|  * Copyright (C) 2011 Texas Instruments | ||||
|  * Author: Chandrabhanu Mahapatra <cmahapatra@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dispc.h" | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M8[8] = { | ||||
| 	{ 0,  0, 128,  0, 0 }, | ||||
| 	{ 0, -4, 123,  9, 0 }, | ||||
| 	{ 0, -4, 108, 24, 0 }, | ||||
| 	{ 0, -2,  87, 43, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 43,  87, -2, 0 }, | ||||
| 	{ 0, 24, 108, -4, 0 }, | ||||
| 	{ 0,  9, 123, -4, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M9[8] = { | ||||
| 	{ 0,  6, 116,  6, 0 }, | ||||
| 	{ 0,  0, 112, 16, 0 }, | ||||
| 	{ 0, -2, 100, 30, 0 }, | ||||
| 	{ 0, -2,  83, 47, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 47,  83, -2, 0 }, | ||||
| 	{ 0, 30, 100, -2, 0 }, | ||||
| 	{ 0, 16, 112,  0, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M10[8] = { | ||||
| 	{ 0, 10, 108, 10, 0 }, | ||||
| 	{ 0,  3, 104, 21, 0 }, | ||||
| 	{ 0,  0,  94, 34, 0 }, | ||||
| 	{ 0, -1,  80, 49, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 49,  80, -1, 0 }, | ||||
| 	{ 0, 34,  94,  0, 0 }, | ||||
| 	{ 0, 21, 104,  3, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M11[8] = { | ||||
| 	{ 0, 14, 100, 14, 0 }, | ||||
| 	{ 0,  6,  98, 24, 0 }, | ||||
| 	{ 0,  2,  90, 36, 0 }, | ||||
| 	{ 0,  0,  78, 50, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 50,  78,  0, 0 }, | ||||
| 	{ 0, 36,  90,  2, 0 }, | ||||
| 	{ 0, 24,  98,  6, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M12[8] = { | ||||
| 	{ 0, 16,  96, 16, 0 }, | ||||
| 	{ 0,  9,  93, 26, 0 }, | ||||
| 	{ 0,  4,  86, 38, 0 }, | ||||
| 	{ 0,  1,  76, 51, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 51,  76,  1, 0 }, | ||||
| 	{ 0, 38,  86,  4, 0 }, | ||||
| 	{ 0, 26,  93,  9, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M13[8] = { | ||||
| 	{ 0, 18,  92, 18, 0 }, | ||||
| 	{ 0, 10,  90, 28, 0 }, | ||||
| 	{ 0,  5,  83, 40, 0 }, | ||||
| 	{ 0,  1,  75, 52, 0 }, | ||||
| 	{ 0, 64,  64,  0, 0 }, | ||||
| 	{ 0, 52,  75,  1, 0 }, | ||||
| 	{ 0, 40,  83,  5, 0 }, | ||||
| 	{ 0, 28,  90, 10, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M14[8] = { | ||||
| 	{ 0, 20, 88, 20, 0 }, | ||||
| 	{ 0, 12, 86, 30, 0 }, | ||||
| 	{ 0,  6, 81, 41, 0 }, | ||||
| 	{ 0,  2, 74, 52, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 52, 74,  2, 0 }, | ||||
| 	{ 0, 41, 81,  6, 0 }, | ||||
| 	{ 0, 30, 86, 12, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M16[8] = { | ||||
| 	{ 0, 22, 84, 22, 0 }, | ||||
| 	{ 0, 14, 82, 32, 0 }, | ||||
| 	{ 0,  8, 78, 42, 0 }, | ||||
| 	{ 0,  3, 72, 53, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 53, 72,  3, 0 }, | ||||
| 	{ 0, 42, 78,  8, 0 }, | ||||
| 	{ 0, 32, 82, 14, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M19[8] = { | ||||
| 	{ 0, 24, 80, 24, 0 }, | ||||
| 	{ 0, 16, 79, 33, 0 }, | ||||
| 	{ 0,  9, 76, 43, 0 }, | ||||
| 	{ 0,  4, 70, 54, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 54, 70,  4, 0 }, | ||||
| 	{ 0, 43, 76,  9, 0 }, | ||||
| 	{ 0, 33, 79, 16, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M22[8] = { | ||||
| 	{ 0, 25, 78, 25, 0 }, | ||||
| 	{ 0, 17, 77, 34, 0 }, | ||||
| 	{ 0, 10, 74, 44, 0 }, | ||||
| 	{ 0,  5, 69, 54, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 54, 69,  5, 0 }, | ||||
| 	{ 0, 44, 74, 10, 0 }, | ||||
| 	{ 0, 34, 77, 17, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M26[8] = { | ||||
| 	{ 0, 26, 76, 26, 0 }, | ||||
| 	{ 0, 19, 74, 35, 0 }, | ||||
| 	{ 0, 11, 72, 45, 0 }, | ||||
| 	{ 0,  5, 69, 54, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 54, 69,  5, 0 }, | ||||
| 	{ 0, 45, 72, 11, 0 }, | ||||
| 	{ 0, 35, 74, 19, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef3_M32[8] = { | ||||
| 	{ 0, 27, 74, 27, 0 }, | ||||
| 	{ 0, 19, 73, 36, 0 }, | ||||
| 	{ 0, 12, 71, 45, 0 }, | ||||
| 	{ 0,  6, 68, 54, 0 }, | ||||
| 	{ 0, 64, 64,  0, 0 }, | ||||
| 	{ 0, 54, 68,  6, 0 }, | ||||
| 	{ 0, 45, 71, 12, 0 }, | ||||
| 	{ 0, 36, 73, 19, 0 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M8[8] = { | ||||
| 	{   0,   0, 128,   0,   0 }, | ||||
| 	{  -2,  14, 125, -10,   1 }, | ||||
| 	{  -6,  33, 114, -15,   2 }, | ||||
| 	{ -10,  55,  98, -16,   1 }, | ||||
| 	{   0, -14,  78,  78, -14 }, | ||||
| 	{   1, -16,  98,  55, -10 }, | ||||
| 	{   2, -15, 114,  33,  -6 }, | ||||
| 	{   1, -10, 125,  14,  -2 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M9[8] = { | ||||
| 	{  -3,  10, 114,  10,  -3 }, | ||||
| 	{  -6,  24, 111,   0,  -1 }, | ||||
| 	{  -8,  40, 103,  -7,   0 }, | ||||
| 	{ -11,  58,  91, -11,   1 }, | ||||
| 	{   0, -12,  76,  76, -12 }, | ||||
| 	{   1, -11,  91,  58, -11 }, | ||||
| 	{   0,  -7, 103,  40,  -8 }, | ||||
| 	{  -1,   0, 111,  24,  -6 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M10[8] = { | ||||
| 	{  -4,  18, 100,  18,  -4 }, | ||||
| 	{  -6,  30,  99,   8,  -3 }, | ||||
| 	{  -8,  44,  93,   0,  -1 }, | ||||
| 	{  -9,  58,  84,  -5,   0 }, | ||||
| 	{   0,  -8,  72,  72,  -8 }, | ||||
| 	{   0,  -5,  84,  58,  -9 }, | ||||
| 	{  -1,   0,  93,  44,  -8 }, | ||||
| 	{  -3,   8,  99,  30,  -6 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M11[8] = { | ||||
| 	{  -5,  23,  92,  23,  -5 }, | ||||
| 	{  -6,  34,  90,  13,  -3 }, | ||||
| 	{  -6,  45,  85,   6,  -2 }, | ||||
| 	{  -6,  57,  78,   0,  -1 }, | ||||
| 	{   0,  -4,  68,  68,  -4 }, | ||||
| 	{  -1,   0,  78,  57,  -6 }, | ||||
| 	{  -2,   6,  85,  45,  -6 }, | ||||
| 	{  -3,  13,  90,  34,  -6 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M12[8] = { | ||||
| 	{  -4,  26,  84,  26,  -4 }, | ||||
| 	{  -5,  36,  82,  18,  -3 }, | ||||
| 	{  -4,  46,  78,  10,  -2 }, | ||||
| 	{  -3,  55,  72,   5,  -1 }, | ||||
| 	{   0,   0,  64,  64,   0 }, | ||||
| 	{  -1,   5,  72,  55,  -3 }, | ||||
| 	{  -2,  10,  78,  46,  -4 }, | ||||
| 	{  -3,  18,  82,  36,  -5 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M13[8] = { | ||||
| 	{  -3,  28,  78,  28,  -3 }, | ||||
| 	{  -3,  37,  76,  21,  -3 }, | ||||
| 	{  -2,  45,  73,  14,  -2 }, | ||||
| 	{   0,  53,  68,   8,  -1 }, | ||||
| 	{   0,   3,  61,  61,   3 }, | ||||
| 	{  -1,   8,  68,  53,   0 }, | ||||
| 	{  -2,  14,  73,  45,  -2 }, | ||||
| 	{  -3,  21,  76,  37,  -3 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M14[8] = { | ||||
| 	{  -2,  30,  72,  30,  -2 }, | ||||
| 	{  -1,  37,  71,  23,  -2 }, | ||||
| 	{   0,  45,  69,  16,  -2 }, | ||||
| 	{   3,  52,  64,  10,  -1 }, | ||||
| 	{   0,   6,  58,  58,   6 }, | ||||
| 	{  -1,  10,  64,  52,   3 }, | ||||
| 	{  -2,  16,  69,  45,   0 }, | ||||
| 	{  -2,  23,  71,  37,  -1 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M16[8] = { | ||||
| 	{   0,  31,  66,  31,   0 }, | ||||
| 	{   1,  38,  65,  25,  -1 }, | ||||
| 	{   3,  44,  62,  20,  -1 }, | ||||
| 	{   6,  49,  59,  14,   0 }, | ||||
| 	{   0,  10,  54,  54,  10 }, | ||||
| 	{   0,  14,  59,  49,   6 }, | ||||
| 	{  -1,  20,  62,  44,   3 }, | ||||
| 	{  -1,  25,  65,  38,   1 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M19[8] = { | ||||
| 	{   3,  32,  58,  32,   3 }, | ||||
| 	{   4,  38,  58,  27,   1 }, | ||||
| 	{   7,  42,  55,  23,   1 }, | ||||
| 	{  10,  46,  54,  18,   0 }, | ||||
| 	{   0,  14,  50,  50,  14 }, | ||||
| 	{   0,  18,  54,  46,  10 }, | ||||
| 	{   1,  23,  55,  42,   7 }, | ||||
| 	{   1,  27,  58,  38,   4 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M22[8] = { | ||||
| 	{   4,  33,  54,  33,   4 }, | ||||
| 	{   6,  37,  54,  28,   3 }, | ||||
| 	{   9,  41,  53,  24,   1 }, | ||||
| 	{  12,  45,  51,  20,   0 }, | ||||
| 	{   0,  16,  48,  48,  16 }, | ||||
| 	{   0,  20,  51,  45,  12 }, | ||||
| 	{   1,  24,  53,  41,   9 }, | ||||
| 	{   3,  28,  54,  37,   6 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M26[8] = { | ||||
| 	{   6,  33,  50,  33,   6 }, | ||||
| 	{   8,  36,  51,  29,   4 }, | ||||
| 	{  11,  40,  50,  25,   2 }, | ||||
| 	{  14,  43,  48,  22,   1 }, | ||||
| 	{   0,  18,  46,  46,  18 }, | ||||
| 	{   1,  22,  48,  43,  14 }, | ||||
| 	{   2,  25,  50,  40,  11 }, | ||||
| 	{   4,  29,  51,  36,   8 }, | ||||
| }; | ||||
| 
 | ||||
| static const struct dispc_coef coef5_M32[8] = { | ||||
| 	{   7,  33,  48,  33,   7 }, | ||||
| 	{  10,  36,  48,  29,   5 }, | ||||
| 	{  13,  39,  47,  26,   3 }, | ||||
| 	{  16,  42,  46,  23,   1 }, | ||||
| 	{   0,  19,  45,  45,  19 }, | ||||
| 	{   1,  23,  46,  42,  16 }, | ||||
| 	{   3,  26,  47,  39,  13 }, | ||||
| 	{   5,  29,  48,  36,  10 }, | ||||
| }; | ||||
| 
 | ||||
| const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps) | ||||
| { | ||||
| 	int i; | ||||
| 	static const struct { | ||||
| 		int Mmin; | ||||
| 		int Mmax; | ||||
| 		const struct dispc_coef *coef_3; | ||||
| 		const struct dispc_coef *coef_5; | ||||
| 	} coefs[] = { | ||||
| 		{ 27, 32, coef3_M32, coef5_M32 }, | ||||
| 		{ 23, 26, coef3_M26, coef5_M26 }, | ||||
| 		{ 20, 22, coef3_M22, coef5_M22 }, | ||||
| 		{ 17, 19, coef3_M19, coef5_M19 }, | ||||
| 		{ 15, 16, coef3_M16, coef5_M16 }, | ||||
| 		{ 14, 14, coef3_M14, coef5_M14 }, | ||||
| 		{ 13, 13, coef3_M13, coef5_M13 }, | ||||
| 		{ 12, 12, coef3_M12, coef5_M12 }, | ||||
| 		{ 11, 11, coef3_M11, coef5_M11 }, | ||||
| 		{ 10, 10, coef3_M10, coef5_M10 }, | ||||
| 		{  9,  9,  coef3_M9,  coef5_M9 }, | ||||
| 		{  4,  8,  coef3_M8,  coef5_M8 }, | ||||
| 		/*
 | ||||
| 		 * When upscaling more than two times, blockiness and outlines | ||||
| 		 * around the image are observed when M8 tables are used. M11, | ||||
| 		 * M16 and M19 tables are used to prevent this. | ||||
| 		 */ | ||||
| 		{  3,  3, coef3_M11, coef5_M11 }, | ||||
| 		{  2,  2, coef3_M16, coef5_M16 }, | ||||
| 		{  0,  1, coef3_M19, coef5_M19 }, | ||||
| 	}; | ||||
| 
 | ||||
| 	inc /= 128; | ||||
| 	for (i = 0; i < ARRAY_SIZE(coefs); ++i) | ||||
| 		if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax) | ||||
| 			return five_taps ? coefs[i].coef_5 : coefs[i].coef_3; | ||||
| 	return NULL; | ||||
| } | ||||
							
								
								
									
										345
									
								
								drivers/video/fbdev/omap2/dss/display-sysfs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								drivers/video/fbdev/omap2/dss/display-sysfs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,345 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "DISPLAY" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/sysfs.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| #include "dss.h" | ||||
| 
 | ||||
| static struct omap_dss_device *to_dss_device_sysfs(struct device *dev) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		if (dssdev->dev == dev) { | ||||
| 			omap_dss_put_device(dssdev); | ||||
| 			return dssdev; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_name_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", | ||||
| 			dssdev->name ? | ||||
| 			dssdev->name : ""); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_enabled_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", | ||||
| 			omapdss_device_is_enabled(dssdev)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_enabled_store(struct device *dev, | ||||
| 		struct device_attribute *attr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int r; | ||||
| 	bool enable; | ||||
| 
 | ||||
| 	r = strtobool(buf, &enable); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (enable == omapdss_device_is_enabled(dssdev)) | ||||
| 		return size; | ||||
| 
 | ||||
| 	if (omapdss_device_is_connected(dssdev) == false) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	if (enable) { | ||||
| 		r = dssdev->driver->enable(dssdev); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} else { | ||||
| 		dssdev->driver->disable(dssdev); | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_tear_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", | ||||
| 			dssdev->driver->get_te ? | ||||
| 			dssdev->driver->get_te(dssdev) : 0); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_tear_store(struct device *dev, | ||||
| 		struct device_attribute *attr, const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int r; | ||||
| 	bool te; | ||||
| 
 | ||||
| 	if (!dssdev->driver->enable_te || !dssdev->driver->get_te) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	r = strtobool(buf, &te); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dssdev->driver->enable_te(dssdev, te); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_timings_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	struct omap_video_timings t; | ||||
| 
 | ||||
| 	if (!dssdev->driver->get_timings) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	dssdev->driver->get_timings(dssdev, &t); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", | ||||
| 			t.pixelclock, | ||||
| 			t.x_res, t.hfp, t.hbp, t.hsw, | ||||
| 			t.y_res, t.vfp, t.vbp, t.vsw); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_timings_store(struct device *dev, | ||||
| 		struct device_attribute *attr, const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	struct omap_video_timings t = dssdev->panel.timings; | ||||
| 	int r, found; | ||||
| 
 | ||||
| 	if (!dssdev->driver->set_timings || !dssdev->driver->check_timings) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	found = 0; | ||||
| #ifdef CONFIG_OMAP2_DSS_VENC | ||||
| 	if (strncmp("pal", buf, 3) == 0) { | ||||
| 		t = omap_dss_pal_timings; | ||||
| 		found = 1; | ||||
| 	} else if (strncmp("ntsc", buf, 4) == 0) { | ||||
| 		t = omap_dss_ntsc_timings; | ||||
| 		found = 1; | ||||
| 	} | ||||
| #endif | ||||
| 	if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", | ||||
| 				&t.pixelclock, | ||||
| 				&t.x_res, &t.hfp, &t.hbp, &t.hsw, | ||||
| 				&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	r = dssdev->driver->check_timings(dssdev, &t); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dssdev->driver->disable(dssdev); | ||||
| 	dssdev->driver->set_timings(dssdev, &t); | ||||
| 	r = dssdev->driver->enable(dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_rotate_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int rotate; | ||||
| 	if (!dssdev->driver->get_rotate) | ||||
| 		return -ENOENT; | ||||
| 	rotate = dssdev->driver->get_rotate(dssdev); | ||||
| 	return snprintf(buf, PAGE_SIZE, "%u\n", rotate); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_rotate_store(struct device *dev, | ||||
| 		struct device_attribute *attr, const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int rot, r; | ||||
| 
 | ||||
| 	if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	r = kstrtoint(buf, 0, &rot); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dssdev->driver->set_rotate(dssdev, rot); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_mirror_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int mirror; | ||||
| 	if (!dssdev->driver->get_mirror) | ||||
| 		return -ENOENT; | ||||
| 	mirror = dssdev->driver->get_mirror(dssdev); | ||||
| 	return snprintf(buf, PAGE_SIZE, "%u\n", mirror); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_mirror_store(struct device *dev, | ||||
| 		struct device_attribute *attr, const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	int r; | ||||
| 	bool mirror; | ||||
| 
 | ||||
| 	if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	r = strtobool(buf, &mirror); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dssdev->driver->set_mirror(dssdev, mirror); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_wss_show(struct device *dev, | ||||
| 		struct device_attribute *attr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	unsigned int wss; | ||||
| 
 | ||||
| 	if (!dssdev->driver->get_wss) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	wss = dssdev->driver->get_wss(dssdev); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); | ||||
| } | ||||
| 
 | ||||
| static ssize_t display_wss_store(struct device *dev, | ||||
| 		struct device_attribute *attr, const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = to_dss_device_sysfs(dev); | ||||
| 	u32 wss; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if (!dssdev->driver->get_wss || !dssdev->driver->set_wss) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	r = kstrtou32(buf, 0, &wss); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (wss > 0xfffff) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	r = dssdev->driver->set_wss(dssdev, wss); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static DEVICE_ATTR(display_name, S_IRUGO, display_name_show, NULL); | ||||
| static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, | ||||
| 		display_enabled_show, display_enabled_store); | ||||
| static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, | ||||
| 		display_tear_show, display_tear_store); | ||||
| static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, | ||||
| 		display_timings_show, display_timings_store); | ||||
| static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, | ||||
| 		display_rotate_show, display_rotate_store); | ||||
| static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, | ||||
| 		display_mirror_show, display_mirror_store); | ||||
| static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, | ||||
| 		display_wss_show, display_wss_store); | ||||
| 
 | ||||
| static const struct attribute *display_sysfs_attrs[] = { | ||||
| 	&dev_attr_display_name.attr, | ||||
| 	&dev_attr_enabled.attr, | ||||
| 	&dev_attr_tear_elim.attr, | ||||
| 	&dev_attr_timings.attr, | ||||
| 	&dev_attr_rotate.attr, | ||||
| 	&dev_attr_mirror.attr, | ||||
| 	&dev_attr_wss.attr, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| int display_init_sysfs(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 	int r; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		struct kobject *kobj = &dssdev->dev->kobj; | ||||
| 
 | ||||
| 		r = sysfs_create_files(kobj, display_sysfs_attrs); | ||||
| 		if (r) { | ||||
| 			DSSERR("failed to create sysfs files\n"); | ||||
| 			goto err; | ||||
| 		} | ||||
| 
 | ||||
| 		r = sysfs_create_link(&pdev->dev.kobj, kobj, dssdev->alias); | ||||
| 		if (r) { | ||||
| 			sysfs_remove_files(kobj, display_sysfs_attrs); | ||||
| 
 | ||||
| 			DSSERR("failed to create sysfs display link\n"); | ||||
| 			goto err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	display_uninit_sysfs(pdev); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| void display_uninit_sysfs(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		sysfs_remove_link(&pdev->dev.kobj, dssdev->alias); | ||||
| 		sysfs_remove_files(&dssdev->dev->kobj, | ||||
| 				display_sysfs_attrs); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										338
									
								
								drivers/video/fbdev/omap2/dss/display.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								drivers/video/fbdev/omap2/dss/display.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,338 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/display.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "DISPLAY" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| void omapdss_default_get_resolution(struct omap_dss_device *dssdev, | ||||
| 			u16 *xres, u16 *yres) | ||||
| { | ||||
| 	*xres = dssdev->panel.timings.x_res; | ||||
| 	*yres = dssdev->panel.timings.y_res; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_default_get_resolution); | ||||
| 
 | ||||
| int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	switch (dssdev->type) { | ||||
| 	case OMAP_DISPLAY_TYPE_DPI: | ||||
| 		if (dssdev->phy.dpi.data_lines == 24) | ||||
| 			return 24; | ||||
| 		else | ||||
| 			return 16; | ||||
| 
 | ||||
| 	case OMAP_DISPLAY_TYPE_DBI: | ||||
| 		if (dssdev->ctrl.pixel_size == 24) | ||||
| 			return 24; | ||||
| 		else | ||||
| 			return 16; | ||||
| 	case OMAP_DISPLAY_TYPE_DSI: | ||||
| 		if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16) | ||||
| 			return 24; | ||||
| 		else | ||||
| 			return 16; | ||||
| 	case OMAP_DISPLAY_TYPE_VENC: | ||||
| 	case OMAP_DISPLAY_TYPE_SDI: | ||||
| 	case OMAP_DISPLAY_TYPE_HDMI: | ||||
| 	case OMAP_DISPLAY_TYPE_DVI: | ||||
| 		return 24; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); | ||||
| 
 | ||||
| void omapdss_default_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	*timings = dssdev->panel.timings; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_default_get_timings); | ||||
| 
 | ||||
| int dss_suspend_all_devices(void) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		if (!dssdev->driver) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { | ||||
| 			dssdev->driver->disable(dssdev); | ||||
| 			dssdev->activate_after_resume = true; | ||||
| 		} else { | ||||
| 			dssdev->activate_after_resume = false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_resume_all_devices(void) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		if (!dssdev->driver) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (dssdev->activate_after_resume) { | ||||
| 			dssdev->driver->enable(dssdev); | ||||
| 			dssdev->activate_after_resume = false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dss_disable_all_devices(void) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	for_each_dss_dev(dssdev) { | ||||
| 		if (!dssdev->driver) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||||
| 			dssdev->driver->disable(dssdev); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static LIST_HEAD(panel_list); | ||||
| static DEFINE_MUTEX(panel_list_mutex); | ||||
| static int disp_num_counter; | ||||
| 
 | ||||
| int omapdss_register_display(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_driver *drv = dssdev->driver; | ||||
| 	int id; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Note: this presumes all the displays are either using DT or non-DT, | ||||
| 	 * which normally should be the case. This also presumes that all | ||||
| 	 * displays either have an DT alias, or none has. | ||||
| 	 */ | ||||
| 
 | ||||
| 	if (dssdev->dev->of_node) { | ||||
| 		id = of_alias_get_id(dssdev->dev->of_node, "display"); | ||||
| 
 | ||||
| 		if (id < 0) | ||||
| 			id = disp_num_counter++; | ||||
| 	} else { | ||||
| 		id = disp_num_counter++; | ||||
| 	} | ||||
| 
 | ||||
| 	snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id); | ||||
| 
 | ||||
| 	/* Use 'label' property for name, if it exists */ | ||||
| 	if (dssdev->dev->of_node) | ||||
| 		of_property_read_string(dssdev->dev->of_node, "label", | ||||
| 			&dssdev->name); | ||||
| 
 | ||||
| 	if (dssdev->name == NULL) | ||||
| 		dssdev->name = dssdev->alias; | ||||
| 
 | ||||
| 	if (drv && drv->get_resolution == NULL) | ||||
| 		drv->get_resolution = omapdss_default_get_resolution; | ||||
| 	if (drv && drv->get_recommended_bpp == NULL) | ||||
| 		drv->get_recommended_bpp = omapdss_default_get_recommended_bpp; | ||||
| 	if (drv && drv->get_timings == NULL) | ||||
| 		drv->get_timings = omapdss_default_get_timings; | ||||
| 
 | ||||
| 	mutex_lock(&panel_list_mutex); | ||||
| 	list_add_tail(&dssdev->panel_list, &panel_list); | ||||
| 	mutex_unlock(&panel_list_mutex); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_register_display); | ||||
| 
 | ||||
| void omapdss_unregister_display(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	mutex_lock(&panel_list_mutex); | ||||
| 	list_del(&dssdev->panel_list); | ||||
| 	mutex_unlock(&panel_list_mutex); | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_unregister_display); | ||||
| 
 | ||||
| struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	if (!try_module_get(dssdev->owner)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (get_device(dssdev->dev) == NULL) { | ||||
| 		module_put(dssdev->owner); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return dssdev; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_device); | ||||
| 
 | ||||
| void omap_dss_put_device(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	put_device(dssdev->dev); | ||||
| 	module_put(dssdev->owner); | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_put_device); | ||||
| 
 | ||||
| /*
 | ||||
|  * ref count of the found device is incremented. | ||||
|  * ref count of from-device is decremented. | ||||
|  */ | ||||
| struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) | ||||
| { | ||||
| 	struct list_head *l; | ||||
| 	struct omap_dss_device *dssdev; | ||||
| 
 | ||||
| 	mutex_lock(&panel_list_mutex); | ||||
| 
 | ||||
| 	if (list_empty(&panel_list)) { | ||||
| 		dssdev = NULL; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (from == NULL) { | ||||
| 		dssdev = list_first_entry(&panel_list, struct omap_dss_device, | ||||
| 				panel_list); | ||||
| 		omap_dss_get_device(dssdev); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	omap_dss_put_device(from); | ||||
| 
 | ||||
| 	list_for_each(l, &panel_list) { | ||||
| 		dssdev = list_entry(l, struct omap_dss_device, panel_list); | ||||
| 		if (dssdev == from) { | ||||
| 			if (list_is_last(l, &panel_list)) { | ||||
| 				dssdev = NULL; | ||||
| 				goto out; | ||||
| 			} | ||||
| 
 | ||||
| 			dssdev = list_entry(l->next, struct omap_dss_device, | ||||
| 					panel_list); | ||||
| 			omap_dss_get_device(dssdev); | ||||
| 			goto out; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	WARN(1, "'from' dssdev not found\n"); | ||||
| 
 | ||||
| 	dssdev = NULL; | ||||
| out: | ||||
| 	mutex_unlock(&panel_list_mutex); | ||||
| 	return dssdev; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_next_device); | ||||
| 
 | ||||
| struct omap_dss_device *omap_dss_find_device(void *data, | ||||
| 		int (*match)(struct omap_dss_device *dssdev, void *data)) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 
 | ||||
| 	while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { | ||||
| 		if (match(dssdev, data)) | ||||
| 			return dssdev; | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_find_device); | ||||
| 
 | ||||
| void videomode_to_omap_video_timings(const struct videomode *vm, | ||||
| 		struct omap_video_timings *ovt) | ||||
| { | ||||
| 	memset(ovt, 0, sizeof(*ovt)); | ||||
| 
 | ||||
| 	ovt->pixelclock = vm->pixelclock; | ||||
| 	ovt->x_res = vm->hactive; | ||||
| 	ovt->hbp = vm->hback_porch; | ||||
| 	ovt->hfp = vm->hfront_porch; | ||||
| 	ovt->hsw = vm->hsync_len; | ||||
| 	ovt->y_res = vm->vactive; | ||||
| 	ovt->vbp = vm->vback_porch; | ||||
| 	ovt->vfp = vm->vfront_porch; | ||||
| 	ovt->vsw = vm->vsync_len; | ||||
| 
 | ||||
| 	ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? | ||||
| 		OMAPDSS_SIG_ACTIVE_HIGH : | ||||
| 		OMAPDSS_SIG_ACTIVE_LOW; | ||||
| 	ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? | ||||
| 		OMAPDSS_SIG_ACTIVE_HIGH : | ||||
| 		OMAPDSS_SIG_ACTIVE_LOW; | ||||
| 	ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ? | ||||
| 		OMAPDSS_SIG_ACTIVE_HIGH : | ||||
| 		OMAPDSS_SIG_ACTIVE_LOW; | ||||
| 	ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? | ||||
| 		OMAPDSS_DRIVE_SIG_RISING_EDGE : | ||||
| 		OMAPDSS_DRIVE_SIG_FALLING_EDGE; | ||||
| 
 | ||||
| 	ovt->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; | ||||
| } | ||||
| EXPORT_SYMBOL(videomode_to_omap_video_timings); | ||||
| 
 | ||||
| void omap_video_timings_to_videomode(const struct omap_video_timings *ovt, | ||||
| 		struct videomode *vm) | ||||
| { | ||||
| 	memset(vm, 0, sizeof(*vm)); | ||||
| 
 | ||||
| 	vm->pixelclock = ovt->pixelclock; | ||||
| 
 | ||||
| 	vm->hactive = ovt->x_res; | ||||
| 	vm->hback_porch = ovt->hbp; | ||||
| 	vm->hfront_porch = ovt->hfp; | ||||
| 	vm->hsync_len = ovt->hsw; | ||||
| 	vm->vactive = ovt->y_res; | ||||
| 	vm->vback_porch = ovt->vbp; | ||||
| 	vm->vfront_porch = ovt->vfp; | ||||
| 	vm->vsync_len = ovt->vsw; | ||||
| 
 | ||||
| 	if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | ||||
| 		vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; | ||||
| 	else | ||||
| 		vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; | ||||
| 
 | ||||
| 	if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | ||||
| 		vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; | ||||
| 	else | ||||
| 		vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; | ||||
| 
 | ||||
| 	if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH) | ||||
| 		vm->flags |= DISPLAY_FLAGS_DE_HIGH; | ||||
| 	else | ||||
| 		vm->flags |= DISPLAY_FLAGS_DE_LOW; | ||||
| 
 | ||||
| 	if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE) | ||||
| 		vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; | ||||
| 	else | ||||
| 		vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_video_timings_to_videomode); | ||||
							
								
								
									
										779
									
								
								drivers/video/fbdev/omap2/dss/dpi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										779
									
								
								drivers/video/fbdev/omap2/dss/dpi.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,779 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dpi.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "DPI" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static struct { | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	struct regulator *vdds_dsi_reg; | ||||
| 	struct platform_device *dsidev; | ||||
| 
 | ||||
| 	struct mutex lock; | ||||
| 
 | ||||
| 	struct omap_video_timings timings; | ||||
| 	struct dss_lcd_mgr_config mgr_config; | ||||
| 	int data_lines; | ||||
| 
 | ||||
| 	struct omap_dss_device output; | ||||
| 
 | ||||
| 	bool port_initialized; | ||||
| } dpi; | ||||
| 
 | ||||
| static struct platform_device *dpi_get_dsidev(enum omap_channel channel) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL | ||||
| 	 * would also be used for DISPC fclk. Meaning, when the DPI output is | ||||
| 	 * disabled, DISPC clock will be disabled, and TV out will stop. | ||||
| 	 */ | ||||
| 	switch (omapdss_get_version()) { | ||||
| 	case OMAPDSS_VER_OMAP24xx: | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES1: | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES3: | ||||
| 	case OMAPDSS_VER_OMAP3630: | ||||
| 	case OMAPDSS_VER_AM35xx: | ||||
| 	case OMAPDSS_VER_AM43xx: | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP4430_ES1: | ||||
| 	case OMAPDSS_VER_OMAP4430_ES2: | ||||
| 	case OMAPDSS_VER_OMAP4: | ||||
| 		switch (channel) { | ||||
| 		case OMAP_DSS_CHANNEL_LCD: | ||||
| 			return dsi_get_dsidev_from_id(0); | ||||
| 		case OMAP_DSS_CHANNEL_LCD2: | ||||
| 			return dsi_get_dsidev_from_id(1); | ||||
| 		default: | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP5: | ||||
| 		switch (channel) { | ||||
| 		case OMAP_DSS_CHANNEL_LCD: | ||||
| 			return dsi_get_dsidev_from_id(0); | ||||
| 		case OMAP_DSS_CHANNEL_LCD3: | ||||
| 			return dsi_get_dsidev_from_id(1); | ||||
| 		default: | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		return NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel) | ||||
| { | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC; | ||||
| 	default: | ||||
| 		/* this shouldn't happen */ | ||||
| 		WARN_ON(1); | ||||
| 		return OMAP_DSS_CLK_SRC_FCK; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct dpi_clk_calc_ctx { | ||||
| 	struct platform_device *dsidev; | ||||
| 
 | ||||
| 	/* inputs */ | ||||
| 
 | ||||
| 	unsigned long pck_min, pck_max; | ||||
| 
 | ||||
| 	/* outputs */ | ||||
| 
 | ||||
| 	struct dsi_clock_info dsi_cinfo; | ||||
| 	unsigned long fck; | ||||
| 	struct dispc_clock_info dispc_cinfo; | ||||
| }; | ||||
| 
 | ||||
| static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, | ||||
| 		unsigned long pck, void *data) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Odd dividers give us uneven duty cycle, causing problem when level | ||||
| 	 * shifted. So skip all odd dividers when the pixel clock is on the | ||||
| 	 * higher side. | ||||
| 	 */ | ||||
| 	if (ctx->pck_min >= 100000000) { | ||||
| 		if (lckd > 1 && lckd % 2 != 0) | ||||
| 			return false; | ||||
| 
 | ||||
| 		if (pckd > 1 && pckd % 2 != 0) | ||||
| 			return false; | ||||
| 	} | ||||
| 
 | ||||
| 	ctx->dispc_cinfo.lck_div = lckd; | ||||
| 	ctx->dispc_cinfo.pck_div = pckd; | ||||
| 	ctx->dispc_cinfo.lck = lck; | ||||
| 	ctx->dispc_cinfo.pck = pck; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, | ||||
| 		void *data) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Odd dividers give us uneven duty cycle, causing problem when level | ||||
| 	 * shifted. So skip all odd dividers when the pixel clock is on the | ||||
| 	 * higher side. | ||||
| 	 */ | ||||
| 	if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 100000000) | ||||
| 		return false; | ||||
| 
 | ||||
| 	ctx->dsi_cinfo.regm_dispc = regm_dispc; | ||||
| 	ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc; | ||||
| 
 | ||||
| 	return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max, | ||||
| 			dpi_calc_dispc_cb, ctx); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint, | ||||
| 		unsigned long pll, | ||||
| 		void *data) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	ctx->dsi_cinfo.regn = regn; | ||||
| 	ctx->dsi_cinfo.regm = regm; | ||||
| 	ctx->dsi_cinfo.fint = fint; | ||||
| 	ctx->dsi_cinfo.clkin4ddr = pll; | ||||
| 
 | ||||
| 	return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min, | ||||
| 			dpi_calc_hsdiv_cb, ctx); | ||||
| } | ||||
| 
 | ||||
| static bool dpi_calc_dss_cb(unsigned long fck, void *data) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	ctx->fck = fck; | ||||
| 
 | ||||
| 	return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, | ||||
| 			dpi_calc_dispc_cb, ctx); | ||||
| } | ||||
| 
 | ||||
| static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) | ||||
| { | ||||
| 	unsigned long clkin; | ||||
| 	unsigned long pll_min, pll_max; | ||||
| 
 | ||||
| 	clkin = dsi_get_pll_clkin(dpi.dsidev); | ||||
| 
 | ||||
| 	memset(ctx, 0, sizeof(*ctx)); | ||||
| 	ctx->dsidev = dpi.dsidev; | ||||
| 	ctx->pck_min = pck - 1000; | ||||
| 	ctx->pck_max = pck + 1000; | ||||
| 	ctx->dsi_cinfo.clkin = clkin; | ||||
| 
 | ||||
| 	pll_min = 0; | ||||
| 	pll_max = 0; | ||||
| 
 | ||||
| 	return dsi_pll_calc(dpi.dsidev, clkin, | ||||
| 			pll_min, pll_max, | ||||
| 			dpi_calc_pll_cb, ctx); | ||||
| } | ||||
| 
 | ||||
| static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * DSS fck gives us very few possibilities, so finding a good pixel | ||||
| 	 * clock may not be possible. We try multiple times to find the clock, | ||||
| 	 * each time widening the pixel clock range we look for, up to | ||||
| 	 * +/- ~15MHz. | ||||
| 	 */ | ||||
| 
 | ||||
| 	for (i = 0; i < 25; ++i) { | ||||
| 		bool ok; | ||||
| 
 | ||||
| 		memset(ctx, 0, sizeof(*ctx)); | ||||
| 		if (pck > 1000 * i * i * i) | ||||
| 			ctx->pck_min = max(pck - 1000 * i * i * i, 0lu); | ||||
| 		else | ||||
| 			ctx->pck_min = 0; | ||||
| 		ctx->pck_max = pck + 1000 * i * i * i; | ||||
| 
 | ||||
| 		ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx); | ||||
| 		if (ok) | ||||
| 			return ok; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| static int dpi_set_dsi_clk(enum omap_channel channel, | ||||
| 		unsigned long pck_req, unsigned long *fck, int *lck_div, | ||||
| 		int *pck_div) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx ctx; | ||||
| 	int r; | ||||
| 	bool ok; | ||||
| 
 | ||||
| 	ok = dpi_dsi_clk_calc(pck_req, &ctx); | ||||
| 	if (!ok) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dss_select_lcd_clk_source(channel, | ||||
| 			dpi_get_alt_clk_src(channel)); | ||||
| 
 | ||||
| 	dpi.mgr_config.clock_info = ctx.dispc_cinfo; | ||||
| 
 | ||||
| 	*fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; | ||||
| 	*lck_div = ctx.dispc_cinfo.lck_div; | ||||
| 	*pck_div = ctx.dispc_cinfo.pck_div; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, | ||||
| 		int *lck_div, int *pck_div) | ||||
| { | ||||
| 	struct dpi_clk_calc_ctx ctx; | ||||
| 	int r; | ||||
| 	bool ok; | ||||
| 
 | ||||
| 	ok = dpi_dss_clk_calc(pck_req, &ctx); | ||||
| 	if (!ok) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	r = dss_set_fck_rate(ctx.fck); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dpi.mgr_config.clock_info = ctx.dispc_cinfo; | ||||
| 
 | ||||
| 	*fck = ctx.fck; | ||||
| 	*lck_div = ctx.dispc_cinfo.lck_div; | ||||
| 	*pck_div = ctx.dispc_cinfo.pck_div; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dpi_set_mode(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	struct omap_video_timings *t = &dpi.timings; | ||||
| 	int lck_div = 0, pck_div = 0; | ||||
| 	unsigned long fck = 0; | ||||
| 	unsigned long pck; | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	if (dpi.dsidev) | ||||
| 		r = dpi_set_dsi_clk(mgr->id, t->pixelclock, &fck, | ||||
| 				&lck_div, &pck_div); | ||||
| 	else | ||||
| 		r = dpi_set_dispc_clk(t->pixelclock, &fck, | ||||
| 				&lck_div, &pck_div); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	pck = fck / lck_div / pck_div; | ||||
| 
 | ||||
| 	if (pck != t->pixelclock) { | ||||
| 		DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n", | ||||
| 			t->pixelclock, pck); | ||||
| 
 | ||||
| 		t->pixelclock = pck; | ||||
| 	} | ||||
| 
 | ||||
| 	dss_mgr_set_timings(mgr, t); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; | ||||
| 
 | ||||
| 	dpi.mgr_config.stallmode = false; | ||||
| 	dpi.mgr_config.fifohandcheck = false; | ||||
| 
 | ||||
| 	dpi.mgr_config.video_port_width = dpi.data_lines; | ||||
| 
 | ||||
| 	dpi.mgr_config.lcden_sig_polarity = 0; | ||||
| 
 | ||||
| 	dss_mgr_set_lcd_config(mgr, &dpi.mgr_config); | ||||
| } | ||||
| 
 | ||||
| static int dpi_display_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &dpi.output; | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&dpi.lock); | ||||
| 
 | ||||
| 	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) { | ||||
| 		DSSERR("no VDSS_DSI regulator\n"); | ||||
| 		r = -ENODEV; | ||||
| 		goto err_no_reg; | ||||
| 	} | ||||
| 
 | ||||
| 	if (out == NULL || out->manager == NULL) { | ||||
| 		DSSERR("failed to enable display: no output/manager\n"); | ||||
| 		r = -ENODEV; | ||||
| 		goto err_no_out_mgr; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) { | ||||
| 		r = regulator_enable(dpi.vdds_dsi_reg); | ||||
| 		if (r) | ||||
| 			goto err_reg_enable; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_get_dispc; | ||||
| 
 | ||||
| 	r = dss_dpi_select_source(out->manager->id); | ||||
| 	if (r) | ||||
| 		goto err_src_sel; | ||||
| 
 | ||||
| 	if (dpi.dsidev) { | ||||
| 		r = dsi_runtime_get(dpi.dsidev); | ||||
| 		if (r) | ||||
| 			goto err_get_dsi; | ||||
| 
 | ||||
| 		r = dsi_pll_init(dpi.dsidev, 0, 1); | ||||
| 		if (r) | ||||
| 			goto err_dsi_pll_init; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dpi_set_mode(out->manager); | ||||
| 	if (r) | ||||
| 		goto err_set_mode; | ||||
| 
 | ||||
| 	dpi_config_lcd_manager(out->manager); | ||||
| 
 | ||||
| 	mdelay(2); | ||||
| 
 | ||||
| 	r = dss_mgr_enable(out->manager); | ||||
| 	if (r) | ||||
| 		goto err_mgr_enable; | ||||
| 
 | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_mgr_enable: | ||||
| err_set_mode: | ||||
| 	if (dpi.dsidev) | ||||
| 		dsi_pll_uninit(dpi.dsidev, true); | ||||
| err_dsi_pll_init: | ||||
| 	if (dpi.dsidev) | ||||
| 		dsi_runtime_put(dpi.dsidev); | ||||
| err_get_dsi: | ||||
| err_src_sel: | ||||
| 	dispc_runtime_put(); | ||||
| err_get_dispc: | ||||
| 	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) | ||||
| 		regulator_disable(dpi.vdds_dsi_reg); | ||||
| err_reg_enable: | ||||
| err_no_out_mgr: | ||||
| err_no_reg: | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void dpi_display_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = dpi.output.manager; | ||||
| 
 | ||||
| 	mutex_lock(&dpi.lock); | ||||
| 
 | ||||
| 	dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 	if (dpi.dsidev) { | ||||
| 		dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); | ||||
| 		dsi_pll_uninit(dpi.dsidev, true); | ||||
| 		dsi_runtime_put(dpi.dsidev); | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) | ||||
| 		regulator_disable(dpi.vdds_dsi_reg); | ||||
| 
 | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| } | ||||
| 
 | ||||
| static void dpi_set_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	DSSDBG("dpi_set_timings\n"); | ||||
| 
 | ||||
| 	mutex_lock(&dpi.lock); | ||||
| 
 | ||||
| 	dpi.timings = *timings; | ||||
| 
 | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| } | ||||
| 
 | ||||
| static void dpi_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	mutex_lock(&dpi.lock); | ||||
| 
 | ||||
| 	*timings = dpi.timings; | ||||
| 
 | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| } | ||||
| 
 | ||||
| static int dpi_check_timings(struct omap_dss_device *dssdev, | ||||
| 			struct omap_video_timings *timings) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = dpi.output.manager; | ||||
| 	int lck_div, pck_div; | ||||
| 	unsigned long fck; | ||||
| 	unsigned long pck; | ||||
| 	struct dpi_clk_calc_ctx ctx; | ||||
| 	bool ok; | ||||
| 
 | ||||
| 	if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (timings->pixelclock == 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (dpi.dsidev) { | ||||
| 		ok = dpi_dsi_clk_calc(timings->pixelclock, &ctx); | ||||
| 		if (!ok) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; | ||||
| 	} else { | ||||
| 		ok = dpi_dss_clk_calc(timings->pixelclock, &ctx); | ||||
| 		if (!ok) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		fck = ctx.fck; | ||||
| 	} | ||||
| 
 | ||||
| 	lck_div = ctx.dispc_cinfo.lck_div; | ||||
| 	pck_div = ctx.dispc_cinfo.pck_div; | ||||
| 
 | ||||
| 	pck = fck / lck_div / pck_div; | ||||
| 
 | ||||
| 	timings->pixelclock = pck; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines) | ||||
| { | ||||
| 	mutex_lock(&dpi.lock); | ||||
| 
 | ||||
| 	dpi.data_lines = data_lines; | ||||
| 
 | ||||
| 	mutex_unlock(&dpi.lock); | ||||
| } | ||||
| 
 | ||||
| static int dpi_verify_dsi_pll(struct platform_device *dsidev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	/* do initial setup with the PLL to see if it is operational */ | ||||
| 
 | ||||
| 	r = dsi_runtime_get(dsidev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dsi_pll_init(dsidev, 0, 1); | ||||
| 	if (r) { | ||||
| 		dsi_runtime_put(dsidev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	dsi_pll_uninit(dsidev, true); | ||||
| 	dsi_runtime_put(dsidev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dpi_init_regulator(void) | ||||
| { | ||||
| 	struct regulator *vdds_dsi; | ||||
| 
 | ||||
| 	if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (dpi.vdds_dsi_reg) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	vdds_dsi = devm_regulator_get(&dpi.pdev->dev, "vdds_dsi"); | ||||
| 	if (IS_ERR(vdds_dsi)) { | ||||
| 		if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) | ||||
| 			DSSERR("can't get VDDS_DSI regulator\n"); | ||||
| 		return PTR_ERR(vdds_dsi); | ||||
| 	} | ||||
| 
 | ||||
| 	dpi.vdds_dsi_reg = vdds_dsi; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dpi_init_pll(void) | ||||
| { | ||||
| 	struct platform_device *dsidev; | ||||
| 
 | ||||
| 	if (dpi.dsidev) | ||||
| 		return; | ||||
| 
 | ||||
| 	dsidev = dpi_get_dsidev(dpi.output.dispc_channel); | ||||
| 	if (!dsidev) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (dpi_verify_dsi_pll(dsidev)) { | ||||
| 		DSSWARN("DSI PLL not operational\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dpi.dsidev = dsidev; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return a hardcoded channel for the DPI output. This should work for | ||||
|  * current use cases, but this can be later expanded to either resolve | ||||
|  * the channel in some more dynamic manner, or get the channel as a user | ||||
|  * parameter. | ||||
|  */ | ||||
| static enum omap_channel dpi_get_channel(void) | ||||
| { | ||||
| 	switch (omapdss_get_version()) { | ||||
| 	case OMAPDSS_VER_OMAP24xx: | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES1: | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES3: | ||||
| 	case OMAPDSS_VER_OMAP3630: | ||||
| 	case OMAPDSS_VER_AM35xx: | ||||
| 	case OMAPDSS_VER_AM43xx: | ||||
| 		return OMAP_DSS_CHANNEL_LCD; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP4430_ES1: | ||||
| 	case OMAPDSS_VER_OMAP4430_ES2: | ||||
| 	case OMAPDSS_VER_OMAP4: | ||||
| 		return OMAP_DSS_CHANNEL_LCD2; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP5: | ||||
| 		return OMAP_DSS_CHANNEL_LCD3; | ||||
| 
 | ||||
| 	default: | ||||
| 		DSSWARN("unsupported DSS version\n"); | ||||
| 		return OMAP_DSS_CHANNEL_LCD; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int dpi_connect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = dpi_init_regulator(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dpi_init_pll(); | ||||
| 
 | ||||
| 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||||
| 	if (!mgr) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = dss_mgr_connect(mgr, dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = omapdss_output_set_device(dssdev, dst); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to connect output to new device: %s\n", | ||||
| 				dst->name); | ||||
| 		dss_mgr_disconnect(mgr, dssdev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dpi_disconnect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	WARN_ON(dst != dssdev->dst); | ||||
| 
 | ||||
| 	if (dst != dssdev->dst) | ||||
| 		return; | ||||
| 
 | ||||
| 	omapdss_output_unset_device(dssdev); | ||||
| 
 | ||||
| 	if (dssdev->manager) | ||||
| 		dss_mgr_disconnect(dssdev->manager, dssdev); | ||||
| } | ||||
| 
 | ||||
| static const struct omapdss_dpi_ops dpi_ops = { | ||||
| 	.connect = dpi_connect, | ||||
| 	.disconnect = dpi_disconnect, | ||||
| 
 | ||||
| 	.enable = dpi_display_enable, | ||||
| 	.disable = dpi_display_disable, | ||||
| 
 | ||||
| 	.check_timings = dpi_check_timings, | ||||
| 	.set_timings = dpi_set_timings, | ||||
| 	.get_timings = dpi_get_timings, | ||||
| 
 | ||||
| 	.set_data_lines = dpi_set_data_lines, | ||||
| }; | ||||
| 
 | ||||
| static void dpi_init_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &dpi.output; | ||||
| 
 | ||||
| 	out->dev = &pdev->dev; | ||||
| 	out->id = OMAP_DSS_OUTPUT_DPI; | ||||
| 	out->output_type = OMAP_DISPLAY_TYPE_DPI; | ||||
| 	out->name = "dpi.0"; | ||||
| 	out->dispc_channel = dpi_get_channel(); | ||||
| 	out->ops.dpi = &dpi_ops; | ||||
| 	out->owner = THIS_MODULE; | ||||
| 
 | ||||
| 	omapdss_register_output(out); | ||||
| } | ||||
| 
 | ||||
| static void __exit dpi_uninit_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &dpi.output; | ||||
| 
 | ||||
| 	omapdss_unregister_output(out); | ||||
| } | ||||
| 
 | ||||
| static int omap_dpi_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	dpi.pdev = pdev; | ||||
| 
 | ||||
| 	mutex_init(&dpi.lock); | ||||
| 
 | ||||
| 	dpi_init_output(pdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __exit omap_dpi_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	dpi_uninit_output(pdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver omap_dpi_driver = { | ||||
| 	.probe		= omap_dpi_probe, | ||||
| 	.remove         = __exit_p(omap_dpi_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_dpi", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init dpi_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&omap_dpi_driver); | ||||
| } | ||||
| 
 | ||||
| void __exit dpi_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omap_dpi_driver); | ||||
| } | ||||
| 
 | ||||
| int __init dpi_init_port(struct platform_device *pdev, struct device_node *port) | ||||
| { | ||||
| 	struct device_node *ep; | ||||
| 	u32 datalines; | ||||
| 	int r; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_next_endpoint(port, NULL); | ||||
| 	if (!ep) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	r = of_property_read_u32(ep, "data-lines", &datalines); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to parse datalines\n"); | ||||
| 		goto err_datalines; | ||||
| 	} | ||||
| 
 | ||||
| 	dpi.data_lines = datalines; | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	dpi.pdev = pdev; | ||||
| 
 | ||||
| 	mutex_init(&dpi.lock); | ||||
| 
 | ||||
| 	dpi_init_output(pdev); | ||||
| 
 | ||||
| 	dpi.port_initialized = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_datalines: | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| void __exit dpi_uninit_port(void) | ||||
| { | ||||
| 	if (!dpi.port_initialized) | ||||
| 		return; | ||||
| 
 | ||||
| 	dpi_uninit_output(dpi.pdev); | ||||
| } | ||||
							
								
								
									
										5769
									
								
								drivers/video/fbdev/omap2/dss/dsi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5769
									
								
								drivers/video/fbdev/omap2/dss/dsi.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										159
									
								
								drivers/video/fbdev/omap2/dss/dss-of.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								drivers/video/fbdev/omap2/dss/dss-of.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2013 Texas Instruments | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/device.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/seq_file.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| struct device_node * | ||||
| omapdss_of_get_next_port(const struct device_node *parent, | ||||
| 			 struct device_node *prev) | ||||
| { | ||||
| 	struct device_node *port = NULL; | ||||
| 
 | ||||
| 	if (!parent) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	if (!prev) { | ||||
| 		struct device_node *ports; | ||||
| 		/*
 | ||||
| 		 * It's the first call, we have to find a port subnode | ||||
| 		 * within this node or within an optional 'ports' node. | ||||
| 		 */ | ||||
| 		ports = of_get_child_by_name(parent, "ports"); | ||||
| 		if (ports) | ||||
| 			parent = ports; | ||||
| 
 | ||||
| 		port = of_get_child_by_name(parent, "port"); | ||||
| 
 | ||||
| 		/* release the 'ports' node */ | ||||
| 		of_node_put(ports); | ||||
| 	} else { | ||||
| 		struct device_node *ports; | ||||
| 
 | ||||
| 		ports = of_get_parent(prev); | ||||
| 		if (!ports) | ||||
| 			return NULL; | ||||
| 
 | ||||
| 		do { | ||||
| 			port = of_get_next_child(ports, prev); | ||||
| 			if (!port) { | ||||
| 				of_node_put(ports); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 			prev = port; | ||||
| 		} while (of_node_cmp(port->name, "port") != 0); | ||||
| 	} | ||||
| 
 | ||||
| 	return port; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(omapdss_of_get_next_port); | ||||
| 
 | ||||
| struct device_node * | ||||
| omapdss_of_get_next_endpoint(const struct device_node *parent, | ||||
| 			     struct device_node *prev) | ||||
| { | ||||
| 	struct device_node *ep = NULL; | ||||
| 
 | ||||
| 	if (!parent) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	do { | ||||
| 		ep = of_get_next_child(parent, prev); | ||||
| 		if (!ep) | ||||
| 			return NULL; | ||||
| 		prev = ep; | ||||
| 	} while (of_node_cmp(ep->name, "endpoint") != 0); | ||||
| 
 | ||||
| 	return ep; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint); | ||||
| 
 | ||||
| static struct device_node * | ||||
| omapdss_of_get_remote_device_node(const struct device_node *node) | ||||
| { | ||||
| 	struct device_node *np; | ||||
| 	int i; | ||||
| 
 | ||||
| 	np = of_parse_phandle(node, "remote-endpoint", 0); | ||||
| 
 | ||||
| 	if (!np) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	np = of_get_next_parent(np); | ||||
| 
 | ||||
| 	for (i = 0; i < 3 && np; ++i) { | ||||
| 		struct property *prop; | ||||
| 
 | ||||
| 		prop = of_find_property(np, "compatible", NULL); | ||||
| 
 | ||||
| 		if (prop) | ||||
| 			return np; | ||||
| 
 | ||||
| 		np = of_get_next_parent(np); | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| struct device_node * | ||||
| omapdss_of_get_first_endpoint(const struct device_node *parent) | ||||
| { | ||||
| 	struct device_node *port, *ep; | ||||
| 
 | ||||
| 	port = omapdss_of_get_next_port(parent, NULL); | ||||
| 
 | ||||
| 	if (!port) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_next_endpoint(port, NULL); | ||||
| 
 | ||||
| 	of_node_put(port); | ||||
| 
 | ||||
| 	return ep; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint); | ||||
| 
 | ||||
| struct omap_dss_device * | ||||
| omapdss_of_find_source_for_first_ep(struct device_node *node) | ||||
| { | ||||
| 	struct device_node *ep; | ||||
| 	struct device_node *src_node; | ||||
| 	struct omap_dss_device *src; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_first_endpoint(node); | ||||
| 	if (!ep) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	src_node = omapdss_of_get_remote_device_node(ep); | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	if (!src_node) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	src = omap_dss_find_output_by_node(src_node); | ||||
| 
 | ||||
| 	of_node_put(src_node); | ||||
| 
 | ||||
| 	if (!src) | ||||
| 		return ERR_PTR(-EPROBE_DEFER); | ||||
| 
 | ||||
| 	return src; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep); | ||||
							
								
								
									
										981
									
								
								drivers/video/fbdev/omap2/dss/dss.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										981
									
								
								drivers/video/fbdev/omap2/dss/dss.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,981 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dss.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "DSS" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/seq_file.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/gfp.h> | ||||
| #include <linux/sizes.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| #define DSS_SZ_REGS			SZ_512 | ||||
| 
 | ||||
| struct dss_reg { | ||||
| 	u16 idx; | ||||
| }; | ||||
| 
 | ||||
| #define DSS_REG(idx)			((const struct dss_reg) { idx }) | ||||
| 
 | ||||
| #define DSS_REVISION			DSS_REG(0x0000) | ||||
| #define DSS_SYSCONFIG			DSS_REG(0x0010) | ||||
| #define DSS_SYSSTATUS			DSS_REG(0x0014) | ||||
| #define DSS_CONTROL			DSS_REG(0x0040) | ||||
| #define DSS_SDI_CONTROL			DSS_REG(0x0044) | ||||
| #define DSS_PLL_CONTROL			DSS_REG(0x0048) | ||||
| #define DSS_SDI_STATUS			DSS_REG(0x005C) | ||||
| 
 | ||||
| #define REG_GET(idx, start, end) \ | ||||
| 	FLD_GET(dss_read_reg(idx), start, end) | ||||
| 
 | ||||
| #define REG_FLD_MOD(idx, val, start, end) \ | ||||
| 	dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) | ||||
| 
 | ||||
| static int dss_runtime_get(void); | ||||
| static void dss_runtime_put(void); | ||||
| 
 | ||||
| struct dss_features { | ||||
| 	u8 fck_div_max; | ||||
| 	u8 dss_fck_multiplier; | ||||
| 	const char *parent_clk_name; | ||||
| 	int (*dpi_select_source)(enum omap_channel channel); | ||||
| }; | ||||
| 
 | ||||
| static struct { | ||||
| 	struct platform_device *pdev; | ||||
| 	void __iomem    *base; | ||||
| 
 | ||||
| 	struct clk	*parent_clk; | ||||
| 	struct clk	*dss_clk; | ||||
| 	unsigned long	dss_clk_rate; | ||||
| 
 | ||||
| 	unsigned long	cache_req_pck; | ||||
| 	unsigned long	cache_prate; | ||||
| 	struct dispc_clock_info cache_dispc_cinfo; | ||||
| 
 | ||||
| 	enum omap_dss_clk_source dsi_clk_source[MAX_NUM_DSI]; | ||||
| 	enum omap_dss_clk_source dispc_clk_source; | ||||
| 	enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; | ||||
| 
 | ||||
| 	bool		ctx_valid; | ||||
| 	u32		ctx[DSS_SZ_REGS / sizeof(u32)]; | ||||
| 
 | ||||
| 	const struct dss_features *feat; | ||||
| } dss; | ||||
| 
 | ||||
| static const char * const dss_generic_clk_source_names[] = { | ||||
| 	[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC]	= "DSI_PLL_HSDIV_DISPC", | ||||
| 	[OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI]	= "DSI_PLL_HSDIV_DSI", | ||||
| 	[OMAP_DSS_CLK_SRC_FCK]			= "DSS_FCK", | ||||
| 	[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC]	= "DSI_PLL2_HSDIV_DISPC", | ||||
| 	[OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI]	= "DSI_PLL2_HSDIV_DSI", | ||||
| }; | ||||
| 
 | ||||
| static inline void dss_write_reg(const struct dss_reg idx, u32 val) | ||||
| { | ||||
| 	__raw_writel(val, dss.base + idx.idx); | ||||
| } | ||||
| 
 | ||||
| static inline u32 dss_read_reg(const struct dss_reg idx) | ||||
| { | ||||
| 	return __raw_readl(dss.base + idx.idx); | ||||
| } | ||||
| 
 | ||||
| #define SR(reg) \ | ||||
| 	dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) | ||||
| #define RR(reg) \ | ||||
| 	dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) | ||||
| 
 | ||||
| static void dss_save_context(void) | ||||
| { | ||||
| 	DSSDBG("dss_save_context\n"); | ||||
| 
 | ||||
| 	SR(CONTROL); | ||||
| 
 | ||||
| 	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & | ||||
| 			OMAP_DISPLAY_TYPE_SDI) { | ||||
| 		SR(SDI_CONTROL); | ||||
| 		SR(PLL_CONTROL); | ||||
| 	} | ||||
| 
 | ||||
| 	dss.ctx_valid = true; | ||||
| 
 | ||||
| 	DSSDBG("context saved\n"); | ||||
| } | ||||
| 
 | ||||
| static void dss_restore_context(void) | ||||
| { | ||||
| 	DSSDBG("dss_restore_context\n"); | ||||
| 
 | ||||
| 	if (!dss.ctx_valid) | ||||
| 		return; | ||||
| 
 | ||||
| 	RR(CONTROL); | ||||
| 
 | ||||
| 	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & | ||||
| 			OMAP_DISPLAY_TYPE_SDI) { | ||||
| 		RR(SDI_CONTROL); | ||||
| 		RR(PLL_CONTROL); | ||||
| 	} | ||||
| 
 | ||||
| 	DSSDBG("context restored\n"); | ||||
| } | ||||
| 
 | ||||
| #undef SR | ||||
| #undef RR | ||||
| 
 | ||||
| void dss_sdi_init(int datapairs) | ||||
| { | ||||
| 	u32 l; | ||||
| 
 | ||||
| 	BUG_ON(datapairs > 3 || datapairs < 1); | ||||
| 
 | ||||
| 	l = dss_read_reg(DSS_SDI_CONTROL); | ||||
| 	l = FLD_MOD(l, 0xf, 19, 15);		/* SDI_PDIV */ | ||||
| 	l = FLD_MOD(l, datapairs-1, 3, 2);	/* SDI_PRSEL */ | ||||
| 	l = FLD_MOD(l, 2, 1, 0);		/* SDI_BWSEL */ | ||||
| 	dss_write_reg(DSS_SDI_CONTROL, l); | ||||
| 
 | ||||
| 	l = dss_read_reg(DSS_PLL_CONTROL); | ||||
| 	l = FLD_MOD(l, 0x7, 25, 22);	/* SDI_PLL_FREQSEL */ | ||||
| 	l = FLD_MOD(l, 0xb, 16, 11);	/* SDI_PLL_REGN */ | ||||
| 	l = FLD_MOD(l, 0xb4, 10, 1);	/* SDI_PLL_REGM */ | ||||
| 	dss_write_reg(DSS_PLL_CONTROL, l); | ||||
| } | ||||
| 
 | ||||
| int dss_sdi_enable(void) | ||||
| { | ||||
| 	unsigned long timeout; | ||||
| 
 | ||||
| 	dispc_pck_free_enable(1); | ||||
| 
 | ||||
| 	/* Reset SDI PLL */ | ||||
| 	REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ | ||||
| 	udelay(1);	/* wait 2x PCLK */ | ||||
| 
 | ||||
| 	/* Lock SDI PLL */ | ||||
| 	REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ | ||||
| 
 | ||||
| 	/* Waiting for PLL lock request to complete */ | ||||
| 	timeout = jiffies + msecs_to_jiffies(500); | ||||
| 	while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) { | ||||
| 		if (time_after_eq(jiffies, timeout)) { | ||||
| 			DSSERR("PLL lock request timed out\n"); | ||||
| 			goto err1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clearing PLL_GO bit */ | ||||
| 	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); | ||||
| 
 | ||||
| 	/* Waiting for PLL to lock */ | ||||
| 	timeout = jiffies + msecs_to_jiffies(500); | ||||
| 	while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) { | ||||
| 		if (time_after_eq(jiffies, timeout)) { | ||||
| 			DSSERR("PLL lock timed out\n"); | ||||
| 			goto err1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_lcd_enable_signal(1); | ||||
| 
 | ||||
| 	/* Waiting for SDI reset to complete */ | ||||
| 	timeout = jiffies + msecs_to_jiffies(500); | ||||
| 	while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) { | ||||
| 		if (time_after_eq(jiffies, timeout)) { | ||||
| 			DSSERR("SDI reset timed out\n"); | ||||
| 			goto err2; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
|  err2: | ||||
| 	dispc_lcd_enable_signal(0); | ||||
|  err1: | ||||
| 	/* Reset SDI PLL */ | ||||
| 	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ | ||||
| 
 | ||||
| 	dispc_pck_free_enable(0); | ||||
| 
 | ||||
| 	return -ETIMEDOUT; | ||||
| } | ||||
| 
 | ||||
| void dss_sdi_disable(void) | ||||
| { | ||||
| 	dispc_lcd_enable_signal(0); | ||||
| 
 | ||||
| 	dispc_pck_free_enable(0); | ||||
| 
 | ||||
| 	/* Reset SDI PLL */ | ||||
| 	REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ | ||||
| } | ||||
| 
 | ||||
| const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src) | ||||
| { | ||||
| 	return dss_generic_clk_source_names[clk_src]; | ||||
| } | ||||
| 
 | ||||
| void dss_dump_clocks(struct seq_file *s) | ||||
| { | ||||
| 	const char *fclk_name, *fclk_real_name; | ||||
| 	unsigned long fclk_rate; | ||||
| 
 | ||||
| 	if (dss_runtime_get()) | ||||
| 		return; | ||||
| 
 | ||||
| 	seq_printf(s, "- DSS -\n"); | ||||
| 
 | ||||
| 	fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK); | ||||
| 	fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); | ||||
| 	fclk_rate = clk_get_rate(dss.dss_clk); | ||||
| 
 | ||||
| 	seq_printf(s, "%s (%s) = %lu\n", | ||||
| 			fclk_name, fclk_real_name, | ||||
| 			fclk_rate); | ||||
| 
 | ||||
| 	dss_runtime_put(); | ||||
| } | ||||
| 
 | ||||
| static void dss_dump_regs(struct seq_file *s) | ||||
| { | ||||
| #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) | ||||
| 
 | ||||
| 	if (dss_runtime_get()) | ||||
| 		return; | ||||
| 
 | ||||
| 	DUMPREG(DSS_REVISION); | ||||
| 	DUMPREG(DSS_SYSCONFIG); | ||||
| 	DUMPREG(DSS_SYSSTATUS); | ||||
| 	DUMPREG(DSS_CONTROL); | ||||
| 
 | ||||
| 	if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & | ||||
| 			OMAP_DISPLAY_TYPE_SDI) { | ||||
| 		DUMPREG(DSS_SDI_CONTROL); | ||||
| 		DUMPREG(DSS_PLL_CONTROL); | ||||
| 		DUMPREG(DSS_SDI_STATUS); | ||||
| 	} | ||||
| 
 | ||||
| 	dss_runtime_put(); | ||||
| #undef DUMPREG | ||||
| } | ||||
| 
 | ||||
| static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) | ||||
| { | ||||
| 	struct platform_device *dsidev; | ||||
| 	int b; | ||||
| 	u8 start, end; | ||||
| 
 | ||||
| 	switch (clk_src) { | ||||
| 	case OMAP_DSS_CLK_SRC_FCK: | ||||
| 		b = 0; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: | ||||
| 		b = 1; | ||||
| 		dsidev = dsi_get_dsidev_from_id(0); | ||||
| 		dsi_wait_pll_hsdiv_dispc_active(dsidev); | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: | ||||
| 		b = 2; | ||||
| 		dsidev = dsi_get_dsidev_from_id(1); | ||||
| 		dsi_wait_pll_hsdiv_dispc_active(dsidev); | ||||
| 		break; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); | ||||
| 
 | ||||
| 	REG_FLD_MOD(DSS_CONTROL, b, start, end);	/* DISPC_CLK_SWITCH */ | ||||
| 
 | ||||
| 	dss.dispc_clk_source = clk_src; | ||||
| } | ||||
| 
 | ||||
| void dss_select_dsi_clk_source(int dsi_module, | ||||
| 		enum omap_dss_clk_source clk_src) | ||||
| { | ||||
| 	struct platform_device *dsidev; | ||||
| 	int b, pos; | ||||
| 
 | ||||
| 	switch (clk_src) { | ||||
| 	case OMAP_DSS_CLK_SRC_FCK: | ||||
| 		b = 0; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI: | ||||
| 		BUG_ON(dsi_module != 0); | ||||
| 		b = 1; | ||||
| 		dsidev = dsi_get_dsidev_from_id(0); | ||||
| 		dsi_wait_pll_hsdiv_dsi_active(dsidev); | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI: | ||||
| 		BUG_ON(dsi_module != 1); | ||||
| 		b = 1; | ||||
| 		dsidev = dsi_get_dsidev_from_id(1); | ||||
| 		dsi_wait_pll_hsdiv_dsi_active(dsidev); | ||||
| 		break; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	pos = dsi_module == 0 ? 1 : 10; | ||||
| 	REG_FLD_MOD(DSS_CONTROL, b, pos, pos);	/* DSIx_CLK_SWITCH */ | ||||
| 
 | ||||
| 	dss.dsi_clk_source[dsi_module] = clk_src; | ||||
| } | ||||
| 
 | ||||
| void dss_select_lcd_clk_source(enum omap_channel channel, | ||||
| 		enum omap_dss_clk_source clk_src) | ||||
| { | ||||
| 	struct platform_device *dsidev; | ||||
| 	int b, ix, pos; | ||||
| 
 | ||||
| 	if (!dss_has_feature(FEAT_LCD_CLK_SRC)) { | ||||
| 		dss_select_dispc_clk_source(clk_src); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (clk_src) { | ||||
| 	case OMAP_DSS_CLK_SRC_FCK: | ||||
| 		b = 0; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: | ||||
| 		BUG_ON(channel != OMAP_DSS_CHANNEL_LCD); | ||||
| 		b = 1; | ||||
| 		dsidev = dsi_get_dsidev_from_id(0); | ||||
| 		dsi_wait_pll_hsdiv_dispc_active(dsidev); | ||||
| 		break; | ||||
| 	case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: | ||||
| 		BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 && | ||||
| 		       channel != OMAP_DSS_CHANNEL_LCD3); | ||||
| 		b = 1; | ||||
| 		dsidev = dsi_get_dsidev_from_id(1); | ||||
| 		dsi_wait_pll_hsdiv_dispc_active(dsidev); | ||||
| 		break; | ||||
| 	default: | ||||
| 		BUG(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : | ||||
| 	     (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19); | ||||
| 	REG_FLD_MOD(DSS_CONTROL, b, pos, pos);	/* LCDx_CLK_SWITCH */ | ||||
| 
 | ||||
| 	ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : | ||||
| 	    (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); | ||||
| 	dss.lcd_clk_source[ix] = clk_src; | ||||
| } | ||||
| 
 | ||||
| enum omap_dss_clk_source dss_get_dispc_clk_source(void) | ||||
| { | ||||
| 	return dss.dispc_clk_source; | ||||
| } | ||||
| 
 | ||||
| enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module) | ||||
| { | ||||
| 	return dss.dsi_clk_source[dsi_module]; | ||||
| } | ||||
| 
 | ||||
| enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) | ||||
| { | ||||
| 	if (dss_has_feature(FEAT_LCD_CLK_SRC)) { | ||||
| 		int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : | ||||
| 			(channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2); | ||||
| 		return dss.lcd_clk_source[ix]; | ||||
| 	} else { | ||||
| 		/* LCD_CLK source is the same as DISPC_FCLK source for
 | ||||
| 		 * OMAP2 and OMAP3 */ | ||||
| 		return dss.dispc_clk_source; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool dss_div_calc(unsigned long pck, unsigned long fck_min, | ||||
| 		dss_div_calc_func func, void *data) | ||||
| { | ||||
| 	int fckd, fckd_start, fckd_stop; | ||||
| 	unsigned long fck; | ||||
| 	unsigned long fck_hw_max; | ||||
| 	unsigned long fckd_hw_max; | ||||
| 	unsigned long prate; | ||||
| 	unsigned m; | ||||
| 
 | ||||
| 	fck_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); | ||||
| 
 | ||||
| 	if (dss.parent_clk == NULL) { | ||||
| 		unsigned pckd; | ||||
| 
 | ||||
| 		pckd = fck_hw_max / pck; | ||||
| 
 | ||||
| 		fck = pck * pckd; | ||||
| 
 | ||||
| 		fck = clk_round_rate(dss.dss_clk, fck); | ||||
| 
 | ||||
| 		return func(fck, data); | ||||
| 	} | ||||
| 
 | ||||
| 	fckd_hw_max = dss.feat->fck_div_max; | ||||
| 
 | ||||
| 	m = dss.feat->dss_fck_multiplier; | ||||
| 	prate = clk_get_rate(dss.parent_clk); | ||||
| 
 | ||||
| 	fck_min = fck_min ? fck_min : 1; | ||||
| 
 | ||||
| 	fckd_start = min(prate * m / fck_min, fckd_hw_max); | ||||
| 	fckd_stop = max(DIV_ROUND_UP(prate * m, fck_hw_max), 1ul); | ||||
| 
 | ||||
| 	for (fckd = fckd_start; fckd >= fckd_stop; --fckd) { | ||||
| 		fck = DIV_ROUND_UP(prate, fckd) * m; | ||||
| 
 | ||||
| 		if (func(fck, data)) | ||||
| 			return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| int dss_set_fck_rate(unsigned long rate) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("set fck to %lu\n", rate); | ||||
| 
 | ||||
| 	r = clk_set_rate(dss.dss_clk, rate); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dss.dss_clk_rate = clk_get_rate(dss.dss_clk); | ||||
| 
 | ||||
| 	WARN_ONCE(dss.dss_clk_rate != rate, | ||||
| 			"clk rate mismatch: %lu != %lu", dss.dss_clk_rate, | ||||
| 			rate); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| unsigned long dss_get_dispc_clk_rate(void) | ||||
| { | ||||
| 	return dss.dss_clk_rate; | ||||
| } | ||||
| 
 | ||||
| static int dss_setup_default_clock(void) | ||||
| { | ||||
| 	unsigned long max_dss_fck, prate; | ||||
| 	unsigned long fck; | ||||
| 	unsigned fck_div; | ||||
| 	int r; | ||||
| 
 | ||||
| 	max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); | ||||
| 
 | ||||
| 	if (dss.parent_clk == NULL) { | ||||
| 		fck = clk_round_rate(dss.dss_clk, max_dss_fck); | ||||
| 	} else { | ||||
| 		prate = clk_get_rate(dss.parent_clk); | ||||
| 
 | ||||
| 		fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier, | ||||
| 				max_dss_fck); | ||||
| 		fck = DIV_ROUND_UP(prate, fck_div) * dss.feat->dss_fck_multiplier; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dss_set_fck_rate(fck); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dss_set_venc_output(enum omap_dss_venc_type type) | ||||
| { | ||||
| 	int l = 0; | ||||
| 
 | ||||
| 	if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) | ||||
| 		l = 0; | ||||
| 	else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) | ||||
| 		l = 1; | ||||
| 	else | ||||
| 		BUG(); | ||||
| 
 | ||||
| 	/* venc out selection. 0 = comp, 1 = svideo */ | ||||
| 	REG_FLD_MOD(DSS_CONTROL, l, 6, 6); | ||||
| } | ||||
| 
 | ||||
| void dss_set_dac_pwrdn_bgz(bool enable) | ||||
| { | ||||
| 	REG_FLD_MOD(DSS_CONTROL, enable, 5, 5);	/* DAC Power-Down Control */ | ||||
| } | ||||
| 
 | ||||
| void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select src) | ||||
| { | ||||
| 	enum omap_display_type dp; | ||||
| 	dp = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); | ||||
| 
 | ||||
| 	/* Complain about invalid selections */ | ||||
| 	WARN_ON((src == DSS_VENC_TV_CLK) && !(dp & OMAP_DISPLAY_TYPE_VENC)); | ||||
| 	WARN_ON((src == DSS_HDMI_M_PCLK) && !(dp & OMAP_DISPLAY_TYPE_HDMI)); | ||||
| 
 | ||||
| 	/* Select only if we have options */ | ||||
| 	if ((dp & OMAP_DISPLAY_TYPE_VENC) && (dp & OMAP_DISPLAY_TYPE_HDMI)) | ||||
| 		REG_FLD_MOD(DSS_CONTROL, src, 15, 15);	/* VENC_HDMI_SWITCH */ | ||||
| } | ||||
| 
 | ||||
| enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) | ||||
| { | ||||
| 	enum omap_display_type displays; | ||||
| 
 | ||||
| 	displays = dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_DIGIT); | ||||
| 	if ((displays & OMAP_DISPLAY_TYPE_HDMI) == 0) | ||||
| 		return DSS_VENC_TV_CLK; | ||||
| 
 | ||||
| 	if ((displays & OMAP_DISPLAY_TYPE_VENC) == 0) | ||||
| 		return DSS_HDMI_M_PCLK; | ||||
| 
 | ||||
| 	return REG_GET(DSS_CONTROL, 15, 15); | ||||
| } | ||||
| 
 | ||||
| static int dss_dpi_select_source_omap2_omap3(enum omap_channel channel) | ||||
| { | ||||
| 	if (channel != OMAP_DSS_CHANNEL_LCD) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_dpi_select_source_omap4(enum omap_channel channel) | ||||
| { | ||||
| 	int val; | ||||
| 
 | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		val = 0; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		val = 1; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	REG_FLD_MOD(DSS_CONTROL, val, 17, 17); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_dpi_select_source_omap5(enum omap_channel channel) | ||||
| { | ||||
| 	int val; | ||||
| 
 | ||||
| 	switch (channel) { | ||||
| 	case OMAP_DSS_CHANNEL_LCD: | ||||
| 		val = 1; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CHANNEL_LCD2: | ||||
| 		val = 2; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CHANNEL_LCD3: | ||||
| 		val = 3; | ||||
| 		break; | ||||
| 	case OMAP_DSS_CHANNEL_DIGIT: | ||||
| 		val = 0; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	REG_FLD_MOD(DSS_CONTROL, val, 17, 16); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_dpi_select_source(enum omap_channel channel) | ||||
| { | ||||
| 	return dss.feat->dpi_select_source(channel); | ||||
| } | ||||
| 
 | ||||
| static int dss_get_clocks(void) | ||||
| { | ||||
| 	struct clk *clk; | ||||
| 
 | ||||
| 	clk = devm_clk_get(&dss.pdev->dev, "fck"); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		DSSERR("can't get clock fck\n"); | ||||
| 		return PTR_ERR(clk); | ||||
| 	} | ||||
| 
 | ||||
| 	dss.dss_clk = clk; | ||||
| 
 | ||||
| 	if (dss.feat->parent_clk_name) { | ||||
| 		clk = clk_get(NULL, dss.feat->parent_clk_name); | ||||
| 		if (IS_ERR(clk)) { | ||||
| 			DSSERR("Failed to get %s\n", dss.feat->parent_clk_name); | ||||
| 			return PTR_ERR(clk); | ||||
| 		} | ||||
| 	} else { | ||||
| 		clk = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	dss.parent_clk = clk; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void dss_put_clocks(void) | ||||
| { | ||||
| 	if (dss.parent_clk) | ||||
| 		clk_put(dss.parent_clk); | ||||
| } | ||||
| 
 | ||||
| static int dss_runtime_get(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("dss_runtime_get\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_get_sync(&dss.pdev->dev); | ||||
| 	WARN_ON(r < 0); | ||||
| 	return r < 0 ? r : 0; | ||||
| } | ||||
| 
 | ||||
| static void dss_runtime_put(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("dss_runtime_put\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_put_sync(&dss.pdev->dev); | ||||
| 	WARN_ON(r < 0 && r != -ENOSYS && r != -EBUSY); | ||||
| } | ||||
| 
 | ||||
| /* DEBUGFS */ | ||||
| #if defined(CONFIG_OMAP2_DSS_DEBUGFS) | ||||
| void dss_debug_dump_clocks(struct seq_file *s) | ||||
| { | ||||
| 	dss_dump_clocks(s); | ||||
| 	dispc_dump_clocks(s); | ||||
| #ifdef CONFIG_OMAP2_DSS_DSI | ||||
| 	dsi_dump_clocks(s); | ||||
| #endif | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static const struct dss_features omap24xx_dss_feats __initconst = { | ||||
| 	/*
 | ||||
| 	 * fck div max is really 16, but the divider range has gaps. The range | ||||
| 	 * from 1 to 6 has no gaps, so let's use that as a max. | ||||
| 	 */ | ||||
| 	.fck_div_max		=	6, | ||||
| 	.dss_fck_multiplier	=	2, | ||||
| 	.parent_clk_name	=	"core_ck", | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3, | ||||
| }; | ||||
| 
 | ||||
| static const struct dss_features omap34xx_dss_feats __initconst = { | ||||
| 	.fck_div_max		=	16, | ||||
| 	.dss_fck_multiplier	=	2, | ||||
| 	.parent_clk_name	=	"dpll4_ck", | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3, | ||||
| }; | ||||
| 
 | ||||
| static const struct dss_features omap3630_dss_feats __initconst = { | ||||
| 	.fck_div_max		=	32, | ||||
| 	.dss_fck_multiplier	=	1, | ||||
| 	.parent_clk_name	=	"dpll4_ck", | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3, | ||||
| }; | ||||
| 
 | ||||
| static const struct dss_features omap44xx_dss_feats __initconst = { | ||||
| 	.fck_div_max		=	32, | ||||
| 	.dss_fck_multiplier	=	1, | ||||
| 	.parent_clk_name	=	"dpll_per_x2_ck", | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap4, | ||||
| }; | ||||
| 
 | ||||
| static const struct dss_features omap54xx_dss_feats __initconst = { | ||||
| 	.fck_div_max		=	64, | ||||
| 	.dss_fck_multiplier	=	1, | ||||
| 	.parent_clk_name	=	"dpll_per_x2_ck", | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap5, | ||||
| }; | ||||
| 
 | ||||
| static const struct dss_features am43xx_dss_feats __initconst = { | ||||
| 	.fck_div_max		=	0, | ||||
| 	.dss_fck_multiplier	=	0, | ||||
| 	.parent_clk_name	=	NULL, | ||||
| 	.dpi_select_source	=	&dss_dpi_select_source_omap2_omap3, | ||||
| }; | ||||
| 
 | ||||
| static int __init dss_init_features(struct platform_device *pdev) | ||||
| { | ||||
| 	const struct dss_features *src; | ||||
| 	struct dss_features *dst; | ||||
| 
 | ||||
| 	dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); | ||||
| 	if (!dst) { | ||||
| 		dev_err(&pdev->dev, "Failed to allocate local DSS Features\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (omapdss_get_version()) { | ||||
| 	case OMAPDSS_VER_OMAP24xx: | ||||
| 		src = &omap24xx_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES1: | ||||
| 	case OMAPDSS_VER_OMAP34xx_ES3: | ||||
| 	case OMAPDSS_VER_AM35xx: | ||||
| 		src = &omap34xx_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP3630: | ||||
| 		src = &omap3630_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP4430_ES1: | ||||
| 	case OMAPDSS_VER_OMAP4430_ES2: | ||||
| 	case OMAPDSS_VER_OMAP4: | ||||
| 		src = &omap44xx_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP5: | ||||
| 		src = &omap54xx_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_AM43xx: | ||||
| 		src = &am43xx_dss_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(dst, src, sizeof(*dst)); | ||||
| 	dss.feat = dst; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __init dss_init_ports(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *parent = pdev->dev.of_node; | ||||
| 	struct device_node *port; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if (parent == NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	port = omapdss_of_get_next_port(parent, NULL); | ||||
| 	if (!port) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	do { | ||||
| 		u32 reg; | ||||
| 
 | ||||
| 		r = of_property_read_u32(port, "reg", ®); | ||||
| 		if (r) | ||||
| 			reg = 0; | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_DPI | ||||
| 		if (reg == 0) | ||||
| 			dpi_init_port(pdev, port); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_SDI | ||||
| 		if (reg == 1) | ||||
| 			sdi_init_port(pdev, port); | ||||
| #endif | ||||
| 
 | ||||
| 	} while ((port = omapdss_of_get_next_port(parent, port)) != NULL); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void __exit dss_uninit_ports(void) | ||||
| { | ||||
| #ifdef CONFIG_OMAP2_DSS_DPI | ||||
| 	dpi_uninit_port(); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_SDI | ||||
| 	sdi_uninit_port(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* DSS HW IP initialisation */ | ||||
| static int __init omap_dsshw_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct resource *dss_mem; | ||||
| 	u32 rev; | ||||
| 	int r; | ||||
| 
 | ||||
| 	dss.pdev = pdev; | ||||
| 
 | ||||
| 	r = dss_init_features(dss.pdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!dss_mem) { | ||||
| 		DSSERR("can't get IORESOURCE_MEM DSS\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	dss.base = devm_ioremap(&pdev->dev, dss_mem->start, | ||||
| 				resource_size(dss_mem)); | ||||
| 	if (!dss.base) { | ||||
| 		DSSERR("can't ioremap DSS\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dss_get_clocks(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dss_setup_default_clock(); | ||||
| 	if (r) | ||||
| 		goto err_setup_clocks; | ||||
| 
 | ||||
| 	pm_runtime_enable(&pdev->dev); | ||||
| 
 | ||||
| 	r = dss_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_runtime_get; | ||||
| 
 | ||||
| 	dss.dss_clk_rate = clk_get_rate(dss.dss_clk); | ||||
| 
 | ||||
| 	/* Select DPLL */ | ||||
| 	REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); | ||||
| 
 | ||||
| 	dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_VENC | ||||
| 	REG_FLD_MOD(DSS_CONTROL, 1, 4, 4);	/* venc dac demen */ | ||||
| 	REG_FLD_MOD(DSS_CONTROL, 1, 3, 3);	/* venc clock 4x enable */ | ||||
| 	REG_FLD_MOD(DSS_CONTROL, 0, 2, 2);	/* venc clock mode = normal */ | ||||
| #endif | ||||
| 	dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||||
| 	dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||||
| 	dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; | ||||
| 	dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; | ||||
| 	dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; | ||||
| 
 | ||||
| 	dss_init_ports(pdev); | ||||
| 
 | ||||
| 	rev = dss_read_reg(DSS_REVISION); | ||||
| 	printk(KERN_INFO "OMAP DSS rev %d.%d\n", | ||||
| 			FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); | ||||
| 
 | ||||
| 	dss_runtime_put(); | ||||
| 
 | ||||
| 	dss_debugfs_create_file("dss", dss_dump_regs); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_runtime_get: | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| err_setup_clocks: | ||||
| 	dss_put_clocks(); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int __exit omap_dsshw_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	dss_uninit_ports(); | ||||
| 
 | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| 
 | ||||
| 	dss_put_clocks(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_runtime_suspend(struct device *dev) | ||||
| { | ||||
| 	dss_save_context(); | ||||
| 	dss_set_min_bus_tput(dev, 0); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_runtime_resume(struct device *dev) | ||||
| { | ||||
| 	int r; | ||||
| 	/*
 | ||||
| 	 * Set an arbitrarily high tput request to ensure OPP100. | ||||
| 	 * What we should really do is to make a request to stay in OPP100, | ||||
| 	 * without any tput requirements, but that is not currently possible | ||||
| 	 * via the PM layer. | ||||
| 	 */ | ||||
| 
 | ||||
| 	r = dss_set_min_bus_tput(dev, 1000000000); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	dss_restore_context(); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops dss_pm_ops = { | ||||
| 	.runtime_suspend = dss_runtime_suspend, | ||||
| 	.runtime_resume = dss_runtime_resume, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id dss_of_match[] = { | ||||
| 	{ .compatible = "ti,omap2-dss", }, | ||||
| 	{ .compatible = "ti,omap3-dss", }, | ||||
| 	{ .compatible = "ti,omap4-dss", }, | ||||
| 	{ .compatible = "ti,omap5-dss", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| MODULE_DEVICE_TABLE(of, dss_of_match); | ||||
| 
 | ||||
| static struct platform_driver omap_dsshw_driver = { | ||||
| 	.remove         = __exit_p(omap_dsshw_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_dss", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.pm	= &dss_pm_ops, | ||||
| 		.of_match_table = dss_of_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init dss_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe); | ||||
| } | ||||
| 
 | ||||
| void dss_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omap_dsshw_driver); | ||||
| } | ||||
							
								
								
									
										441
									
								
								drivers/video/fbdev/omap2/dss/dss.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								drivers/video/fbdev/omap2/dss/dss.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,441 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dss.h | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __OMAP2_DSS_H | ||||
| #define __OMAP2_DSS_H | ||||
| 
 | ||||
| #include <linux/interrupt.h> | ||||
| 
 | ||||
| #ifdef pr_fmt | ||||
| #undef pr_fmt | ||||
| #endif | ||||
| 
 | ||||
| #ifdef DSS_SUBSYS_NAME | ||||
| #define pr_fmt(fmt) DSS_SUBSYS_NAME ": " fmt | ||||
| #else | ||||
| #define pr_fmt(fmt) fmt | ||||
| #endif | ||||
| 
 | ||||
| #define DSSDBG(format, ...) \ | ||||
| 	pr_debug(format, ## __VA_ARGS__) | ||||
| 
 | ||||
| #ifdef DSS_SUBSYS_NAME | ||||
| #define DSSERR(format, ...) \ | ||||
| 	printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \ | ||||
| 	## __VA_ARGS__) | ||||
| #else | ||||
| #define DSSERR(format, ...) \ | ||||
| 	printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__) | ||||
| #endif | ||||
| 
 | ||||
| #ifdef DSS_SUBSYS_NAME | ||||
| #define DSSINFO(format, ...) \ | ||||
| 	printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \ | ||||
| 	## __VA_ARGS__) | ||||
| #else | ||||
| #define DSSINFO(format, ...) \ | ||||
| 	printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__) | ||||
| #endif | ||||
| 
 | ||||
| #ifdef DSS_SUBSYS_NAME | ||||
| #define DSSWARN(format, ...) \ | ||||
| 	printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \ | ||||
| 	## __VA_ARGS__) | ||||
| #else | ||||
| #define DSSWARN(format, ...) \ | ||||
| 	printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__) | ||||
| #endif | ||||
| 
 | ||||
| /* OMAP TRM gives bitfields as start:end, where start is the higher bit
 | ||||
|    number. For example 7:0 */ | ||||
| #define FLD_MASK(start, end)	(((1 << ((start) - (end) + 1)) - 1) << (end)) | ||||
| #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) | ||||
| #define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) | ||||
| #define FLD_MOD(orig, val, start, end) \ | ||||
| 	(((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) | ||||
| 
 | ||||
| enum dss_io_pad_mode { | ||||
| 	DSS_IO_PAD_MODE_RESET, | ||||
| 	DSS_IO_PAD_MODE_RFBI, | ||||
| 	DSS_IO_PAD_MODE_BYPASS, | ||||
| }; | ||||
| 
 | ||||
| enum dss_hdmi_venc_clk_source_select { | ||||
| 	DSS_VENC_TV_CLK = 0, | ||||
| 	DSS_HDMI_M_PCLK = 1, | ||||
| }; | ||||
| 
 | ||||
| enum dss_dsi_content_type { | ||||
| 	DSS_DSI_CONTENT_DCS, | ||||
| 	DSS_DSI_CONTENT_GENERIC, | ||||
| }; | ||||
| 
 | ||||
| enum dss_writeback_channel { | ||||
| 	DSS_WB_LCD1_MGR =	0, | ||||
| 	DSS_WB_LCD2_MGR =	1, | ||||
| 	DSS_WB_TV_MGR =		2, | ||||
| 	DSS_WB_OVL0 =		3, | ||||
| 	DSS_WB_OVL1 =		4, | ||||
| 	DSS_WB_OVL2 =		5, | ||||
| 	DSS_WB_OVL3 =		6, | ||||
| 	DSS_WB_LCD3_MGR =	7, | ||||
| }; | ||||
| 
 | ||||
| struct dispc_clock_info { | ||||
| 	/* rates that we get with dividers below */ | ||||
| 	unsigned long lck; | ||||
| 	unsigned long pck; | ||||
| 
 | ||||
| 	/* dividers */ | ||||
| 	u16 lck_div; | ||||
| 	u16 pck_div; | ||||
| }; | ||||
| 
 | ||||
| struct dsi_clock_info { | ||||
| 	/* rates that we get with dividers below */ | ||||
| 	unsigned long fint; | ||||
| 	unsigned long clkin4ddr; | ||||
| 	unsigned long clkin; | ||||
| 	unsigned long dsi_pll_hsdiv_dispc_clk;	/* OMAP3: DSI1_PLL_CLK
 | ||||
| 						 * OMAP4: PLLx_CLK1 */ | ||||
| 	unsigned long dsi_pll_hsdiv_dsi_clk;	/* OMAP3: DSI2_PLL_CLK
 | ||||
| 						 * OMAP4: PLLx_CLK2 */ | ||||
| 	unsigned long lp_clk; | ||||
| 
 | ||||
| 	/* dividers */ | ||||
| 	u16 regn; | ||||
| 	u16 regm; | ||||
| 	u16 regm_dispc;	/* OMAP3: REGM3
 | ||||
| 			 * OMAP4: REGM4 */ | ||||
| 	u16 regm_dsi;	/* OMAP3: REGM4
 | ||||
| 			 * OMAP4: REGM5 */ | ||||
| 	u16 lp_clk_div; | ||||
| }; | ||||
| 
 | ||||
| struct dss_lcd_mgr_config { | ||||
| 	enum dss_io_pad_mode io_pad_mode; | ||||
| 
 | ||||
| 	bool stallmode; | ||||
| 	bool fifohandcheck; | ||||
| 
 | ||||
| 	struct dispc_clock_info clock_info; | ||||
| 
 | ||||
| 	int video_port_width; | ||||
| 
 | ||||
| 	int lcden_sig_polarity; | ||||
| }; | ||||
| 
 | ||||
| struct seq_file; | ||||
| struct platform_device; | ||||
| 
 | ||||
| /* core */ | ||||
| struct platform_device *dss_get_core_pdev(void); | ||||
| int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); | ||||
| void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); | ||||
| int dss_set_min_bus_tput(struct device *dev, unsigned long tput); | ||||
| int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); | ||||
| 
 | ||||
| /* display */ | ||||
| int dss_suspend_all_devices(void); | ||||
| int dss_resume_all_devices(void); | ||||
| void dss_disable_all_devices(void); | ||||
| 
 | ||||
| int display_init_sysfs(struct platform_device *pdev); | ||||
| void display_uninit_sysfs(struct platform_device *pdev); | ||||
| 
 | ||||
| /* manager */ | ||||
| int dss_init_overlay_managers(void); | ||||
| void dss_uninit_overlay_managers(void); | ||||
| int dss_init_overlay_managers_sysfs(struct platform_device *pdev); | ||||
| void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev); | ||||
| int dss_mgr_simple_check(struct omap_overlay_manager *mgr, | ||||
| 		const struct omap_overlay_manager_info *info); | ||||
| int dss_mgr_check_timings(struct omap_overlay_manager *mgr, | ||||
| 		const struct omap_video_timings *timings); | ||||
| int dss_mgr_check(struct omap_overlay_manager *mgr, | ||||
| 		struct omap_overlay_manager_info *info, | ||||
| 		const struct omap_video_timings *mgr_timings, | ||||
| 		const struct dss_lcd_mgr_config *config, | ||||
| 		struct omap_overlay_info **overlay_infos); | ||||
| 
 | ||||
| static inline bool dss_mgr_is_lcd(enum omap_channel id) | ||||
| { | ||||
| 	if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 || | ||||
| 			id == OMAP_DSS_CHANNEL_LCD3) | ||||
| 		return true; | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| 
 | ||||
| int dss_manager_kobj_init(struct omap_overlay_manager *mgr, | ||||
| 		struct platform_device *pdev); | ||||
| void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr); | ||||
| 
 | ||||
| /* overlay */ | ||||
| void dss_init_overlays(struct platform_device *pdev); | ||||
| void dss_uninit_overlays(struct platform_device *pdev); | ||||
| void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); | ||||
| int dss_ovl_simple_check(struct omap_overlay *ovl, | ||||
| 		const struct omap_overlay_info *info); | ||||
| int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, | ||||
| 		const struct omap_video_timings *mgr_timings); | ||||
| bool dss_ovl_use_replication(struct dss_lcd_mgr_config config, | ||||
| 		enum omap_color_mode mode); | ||||
| int dss_overlay_kobj_init(struct omap_overlay *ovl, | ||||
| 		struct platform_device *pdev); | ||||
| void dss_overlay_kobj_uninit(struct omap_overlay *ovl); | ||||
| 
 | ||||
| /* DSS */ | ||||
| int dss_init_platform_driver(void) __init; | ||||
| void dss_uninit_platform_driver(void); | ||||
| 
 | ||||
| unsigned long dss_get_dispc_clk_rate(void); | ||||
| int dss_dpi_select_source(enum omap_channel channel); | ||||
| void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); | ||||
| enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); | ||||
| const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); | ||||
| void dss_dump_clocks(struct seq_file *s); | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP2_DSS_DEBUGFS) | ||||
| void dss_debug_dump_clocks(struct seq_file *s); | ||||
| #endif | ||||
| 
 | ||||
| void dss_sdi_init(int datapairs); | ||||
| int dss_sdi_enable(void); | ||||
| void dss_sdi_disable(void); | ||||
| 
 | ||||
| void dss_select_dsi_clk_source(int dsi_module, | ||||
| 		enum omap_dss_clk_source clk_src); | ||||
| void dss_select_lcd_clk_source(enum omap_channel channel, | ||||
| 		enum omap_dss_clk_source clk_src); | ||||
| enum omap_dss_clk_source dss_get_dispc_clk_source(void); | ||||
| enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module); | ||||
| enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel); | ||||
| 
 | ||||
| void dss_set_venc_output(enum omap_dss_venc_type type); | ||||
| void dss_set_dac_pwrdn_bgz(bool enable); | ||||
| 
 | ||||
| int dss_set_fck_rate(unsigned long rate); | ||||
| 
 | ||||
| typedef bool (*dss_div_calc_func)(unsigned long fck, void *data); | ||||
| bool dss_div_calc(unsigned long pck, unsigned long fck_min, | ||||
| 		dss_div_calc_func func, void *data); | ||||
| 
 | ||||
| /* SDI */ | ||||
| int sdi_init_platform_driver(void) __init; | ||||
| void sdi_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init; | ||||
| void sdi_uninit_port(void) __exit; | ||||
| 
 | ||||
| /* DSI */ | ||||
| 
 | ||||
| typedef bool (*dsi_pll_calc_func)(int regn, int regm, unsigned long fint, | ||||
| 		unsigned long pll, void *data); | ||||
| typedef bool (*dsi_hsdiv_calc_func)(int regm_dispc, unsigned long dispc, | ||||
| 		void *data); | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_DSI | ||||
| 
 | ||||
| struct dentry; | ||||
| struct file_operations; | ||||
| 
 | ||||
| int dsi_init_platform_driver(void) __init; | ||||
| void dsi_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| int dsi_runtime_get(struct platform_device *dsidev); | ||||
| void dsi_runtime_put(struct platform_device *dsidev); | ||||
| 
 | ||||
| void dsi_dump_clocks(struct seq_file *s); | ||||
| 
 | ||||
| void dsi_irq_handler(void); | ||||
| u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); | ||||
| 
 | ||||
| unsigned long dsi_get_pll_clkin(struct platform_device *dsidev); | ||||
| 
 | ||||
| bool dsi_hsdiv_calc(struct platform_device *dsidev, unsigned long pll, | ||||
| 		unsigned long out_min, dsi_hsdiv_calc_func func, void *data); | ||||
| bool dsi_pll_calc(struct platform_device *dsidev, unsigned long clkin, | ||||
| 		unsigned long pll_min, unsigned long pll_max, | ||||
| 		dsi_pll_calc_func func, void *data); | ||||
| 
 | ||||
| unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); | ||||
| int dsi_pll_set_clock_div(struct platform_device *dsidev, | ||||
| 		struct dsi_clock_info *cinfo); | ||||
| int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, | ||||
| 		bool enable_hsdiv); | ||||
| void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); | ||||
| void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); | ||||
| void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); | ||||
| struct platform_device *dsi_get_dsidev_from_id(int module); | ||||
| #else | ||||
| static inline int dsi_runtime_get(struct platform_device *dsidev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| static inline void dsi_runtime_put(struct platform_device *dsidev) | ||||
| { | ||||
| } | ||||
| static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) | ||||
| { | ||||
| 	WARN("%s: DSI not compiled in, returning pixel_size as 0\n", __func__); | ||||
| 	return 0; | ||||
| } | ||||
| static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) | ||||
| { | ||||
| 	WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); | ||||
| 	return 0; | ||||
| } | ||||
| static inline int dsi_pll_set_clock_div(struct platform_device *dsidev, | ||||
| 		struct dsi_clock_info *cinfo) | ||||
| { | ||||
| 	WARN("%s: DSI not compiled in\n", __func__); | ||||
| 	return -ENODEV; | ||||
| } | ||||
| static inline int dsi_pll_init(struct platform_device *dsidev, | ||||
| 		bool enable_hsclk, bool enable_hsdiv) | ||||
| { | ||||
| 	WARN("%s: DSI not compiled in\n", __func__); | ||||
| 	return -ENODEV; | ||||
| } | ||||
| static inline void dsi_pll_uninit(struct platform_device *dsidev, | ||||
| 		bool disconnect_lanes) | ||||
| { | ||||
| } | ||||
| static inline void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) | ||||
| { | ||||
| } | ||||
| static inline void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) | ||||
| { | ||||
| } | ||||
| static inline struct platform_device *dsi_get_dsidev_from_id(int module) | ||||
| { | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static inline unsigned long dsi_get_pll_clkin(struct platform_device *dsidev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline bool dsi_hsdiv_calc(struct platform_device *dsidev, | ||||
| 		unsigned long pll, unsigned long out_min, | ||||
| 		dsi_hsdiv_calc_func func, void *data) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static inline bool dsi_pll_calc(struct platform_device *dsidev, | ||||
| 		unsigned long clkin, | ||||
| 		unsigned long pll_min, unsigned long pll_max, | ||||
| 		dsi_pll_calc_func func, void *data) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /* DPI */ | ||||
| int dpi_init_platform_driver(void) __init; | ||||
| void dpi_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init; | ||||
| void dpi_uninit_port(void) __exit; | ||||
| 
 | ||||
| /* DISPC */ | ||||
| int dispc_init_platform_driver(void) __init; | ||||
| void dispc_uninit_platform_driver(void) __exit; | ||||
| void dispc_dump_clocks(struct seq_file *s); | ||||
| 
 | ||||
| void dispc_enable_sidle(void); | ||||
| void dispc_disable_sidle(void); | ||||
| 
 | ||||
| void dispc_lcd_enable_signal(bool enable); | ||||
| void dispc_pck_free_enable(bool enable); | ||||
| void dispc_enable_fifomerge(bool enable); | ||||
| void dispc_enable_gamma_table(bool enable); | ||||
| void dispc_set_loadmode(enum omap_dss_load_mode mode); | ||||
| 
 | ||||
| typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck, | ||||
| 		unsigned long pck, void *data); | ||||
| bool dispc_div_calc(unsigned long dispc, | ||||
| 		unsigned long pck_min, unsigned long pck_max, | ||||
| 		dispc_div_calc_func func, void *data); | ||||
| 
 | ||||
| bool dispc_mgr_timings_ok(enum omap_channel channel, | ||||
| 		const struct omap_video_timings *timings); | ||||
| unsigned long dispc_fclk_rate(void); | ||||
| int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, | ||||
| 		struct dispc_clock_info *cinfo); | ||||
| 
 | ||||
| 
 | ||||
| void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); | ||||
| void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, | ||||
| 		u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, | ||||
| 		bool manual_update); | ||||
| 
 | ||||
| unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); | ||||
| unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); | ||||
| unsigned long dispc_core_clk_rate(void); | ||||
| void dispc_mgr_set_clock_div(enum omap_channel channel, | ||||
| 		const struct dispc_clock_info *cinfo); | ||||
| int dispc_mgr_get_clock_div(enum omap_channel channel, | ||||
| 		struct dispc_clock_info *cinfo); | ||||
| void dispc_set_tv_pclk(unsigned long pclk); | ||||
| 
 | ||||
| u32 dispc_wb_get_framedone_irq(void); | ||||
| bool dispc_wb_go_busy(void); | ||||
| void dispc_wb_go(void); | ||||
| void dispc_wb_enable(bool enable); | ||||
| bool dispc_wb_is_enabled(void); | ||||
| void dispc_wb_set_channel_in(enum dss_writeback_channel channel); | ||||
| int dispc_wb_setup(const struct omap_dss_writeback_info *wi, | ||||
| 		bool mem_to_mem, const struct omap_video_timings *timings); | ||||
| 
 | ||||
| /* VENC */ | ||||
| int venc_init_platform_driver(void) __init; | ||||
| void venc_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| /* HDMI */ | ||||
| int hdmi4_init_platform_driver(void) __init; | ||||
| void hdmi4_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| int hdmi5_init_platform_driver(void) __init; | ||||
| void hdmi5_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| /* RFBI */ | ||||
| int rfbi_init_platform_driver(void) __init; | ||||
| void rfbi_uninit_platform_driver(void) __exit; | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS | ||||
| static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) | ||||
| { | ||||
| 	int b; | ||||
| 	for (b = 0; b < 32; ++b) { | ||||
| 		if (irqstatus & (1 << b)) | ||||
| 			irq_arr[b]++; | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										1003
									
								
								drivers/video/fbdev/omap2/dss/dss_features.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1003
									
								
								drivers/video/fbdev/omap2/dss/dss_features.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										117
									
								
								drivers/video/fbdev/omap2/dss/dss_features.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								drivers/video/fbdev/omap2/dss/dss_features.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/dss_features.h | ||||
|  * | ||||
|  * Copyright (C) 2010 Texas Instruments | ||||
|  * Author: Archit Taneja <archit@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __OMAP2_DSS_FEATURES_H | ||||
| #define __OMAP2_DSS_FEATURES_H | ||||
| 
 | ||||
| #define MAX_DSS_MANAGERS	4 | ||||
| #define MAX_DSS_OVERLAYS	4 | ||||
| #define MAX_DSS_LCD_MANAGERS	3 | ||||
| #define MAX_NUM_DSI		2 | ||||
| 
 | ||||
| /* DSS has feature id */ | ||||
| enum dss_feat_id { | ||||
| 	FEAT_LCDENABLEPOL, | ||||
| 	FEAT_LCDENABLESIGNAL, | ||||
| 	FEAT_PCKFREEENABLE, | ||||
| 	FEAT_FUNCGATED, | ||||
| 	FEAT_MGR_LCD2, | ||||
| 	FEAT_MGR_LCD3, | ||||
| 	FEAT_LINEBUFFERSPLIT, | ||||
| 	FEAT_ROWREPEATENABLE, | ||||
| 	FEAT_RESIZECONF, | ||||
| 	/* Independent core clk divider */ | ||||
| 	FEAT_CORE_CLK_DIV, | ||||
| 	FEAT_LCD_CLK_SRC, | ||||
| 	/* DSI-PLL power command 0x3 is not working */ | ||||
| 	FEAT_DSI_PLL_PWR_BUG, | ||||
| 	FEAT_DSI_PLL_FREQSEL, | ||||
| 	FEAT_DSI_DCS_CMD_CONFIG_VC, | ||||
| 	FEAT_DSI_VC_OCP_WIDTH, | ||||
| 	FEAT_DSI_REVERSE_TXCLKESC, | ||||
| 	FEAT_DSI_GNQ, | ||||
| 	FEAT_DPI_USES_VDDS_DSI, | ||||
| 	FEAT_HDMI_CTS_SWMODE, | ||||
| 	FEAT_HDMI_AUDIO_USE_MCLK, | ||||
| 	FEAT_HANDLE_UV_SEPARATE, | ||||
| 	FEAT_ATTR2, | ||||
| 	FEAT_VENC_REQUIRES_TV_DAC_CLK, | ||||
| 	FEAT_CPR, | ||||
| 	FEAT_PRELOAD, | ||||
| 	FEAT_FIR_COEF_V, | ||||
| 	FEAT_ALPHA_FIXED_ZORDER, | ||||
| 	FEAT_ALPHA_FREE_ZORDER, | ||||
| 	FEAT_FIFO_MERGE, | ||||
| 	/* An unknown HW bug causing the normal FIFO thresholds not to work */ | ||||
| 	FEAT_OMAP3_DSI_FIFO_BUG, | ||||
| 	FEAT_BURST_2D, | ||||
| 	FEAT_DSI_PLL_SELFREQDCO, | ||||
| 	FEAT_DSI_PLL_REFSEL, | ||||
| 	FEAT_DSI_PHY_DCC, | ||||
| 	FEAT_MFLAG, | ||||
| }; | ||||
| 
 | ||||
| /* DSS register field id */ | ||||
| enum dss_feat_reg_field { | ||||
| 	FEAT_REG_FIRHINC, | ||||
| 	FEAT_REG_FIRVINC, | ||||
| 	FEAT_REG_FIFOHIGHTHRESHOLD, | ||||
| 	FEAT_REG_FIFOLOWTHRESHOLD, | ||||
| 	FEAT_REG_FIFOSIZE, | ||||
| 	FEAT_REG_HORIZONTALACCU, | ||||
| 	FEAT_REG_VERTICALACCU, | ||||
| 	FEAT_REG_DISPC_CLK_SWITCH, | ||||
| 	FEAT_REG_DSIPLL_REGN, | ||||
| 	FEAT_REG_DSIPLL_REGM, | ||||
| 	FEAT_REG_DSIPLL_REGM_DISPC, | ||||
| 	FEAT_REG_DSIPLL_REGM_DSI, | ||||
| }; | ||||
| 
 | ||||
| enum dss_range_param { | ||||
| 	FEAT_PARAM_DSS_FCK, | ||||
| 	FEAT_PARAM_DSS_PCD, | ||||
| 	FEAT_PARAM_DSIPLL_REGN, | ||||
| 	FEAT_PARAM_DSIPLL_REGM, | ||||
| 	FEAT_PARAM_DSIPLL_REGM_DISPC, | ||||
| 	FEAT_PARAM_DSIPLL_REGM_DSI, | ||||
| 	FEAT_PARAM_DSIPLL_FINT, | ||||
| 	FEAT_PARAM_DSIPLL_LPDIV, | ||||
| 	FEAT_PARAM_DSI_FCK, | ||||
| 	FEAT_PARAM_DOWNSCALE, | ||||
| 	FEAT_PARAM_LINEWIDTH, | ||||
| }; | ||||
| 
 | ||||
| /* DSS Feature Functions */ | ||||
| int dss_feat_get_num_wbs(void); | ||||
| unsigned long dss_feat_get_param_min(enum dss_range_param param); | ||||
| unsigned long dss_feat_get_param_max(enum dss_range_param param); | ||||
| enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane plane); | ||||
| bool dss_feat_color_mode_supported(enum omap_plane plane, | ||||
| 		enum omap_color_mode color_mode); | ||||
| const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); | ||||
| 
 | ||||
| u32 dss_feat_get_buffer_size_unit(void);	/* in bytes */ | ||||
| u32 dss_feat_get_burst_size_unit(void);		/* in bytes */ | ||||
| 
 | ||||
| bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type); | ||||
| 
 | ||||
| bool dss_has_feature(enum dss_feat_id id); | ||||
| void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); | ||||
| void dss_features_init(enum omapdss_version version); | ||||
| #endif | ||||
							
								
								
									
										350
									
								
								drivers/video/fbdev/omap2/dss/hdmi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								drivers/video/fbdev/omap2/dss/hdmi.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,350 @@ | |||
| /*
 | ||||
|  * HDMI driver definition for TI OMAP4 Processor. | ||||
|  * | ||||
|  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
 | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _HDMI_H | ||||
| #define _HDMI_H | ||||
| 
 | ||||
| #include <linux/delay.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/hdmi.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| 
 | ||||
| /* HDMI Wrapper */ | ||||
| 
 | ||||
| #define HDMI_WP_REVISION			0x0 | ||||
| #define HDMI_WP_SYSCONFIG			0x10 | ||||
| #define HDMI_WP_IRQSTATUS_RAW			0x24 | ||||
| #define HDMI_WP_IRQSTATUS			0x28 | ||||
| #define HDMI_WP_IRQENABLE_SET			0x2C | ||||
| #define HDMI_WP_IRQENABLE_CLR			0x30 | ||||
| #define HDMI_WP_IRQWAKEEN			0x34 | ||||
| #define HDMI_WP_PWR_CTRL			0x40 | ||||
| #define HDMI_WP_DEBOUNCE			0x44 | ||||
| #define HDMI_WP_VIDEO_CFG			0x50 | ||||
| #define HDMI_WP_VIDEO_SIZE			0x60 | ||||
| #define HDMI_WP_VIDEO_TIMING_H			0x68 | ||||
| #define HDMI_WP_VIDEO_TIMING_V			0x6C | ||||
| #define HDMI_WP_CLK				0x70 | ||||
| #define HDMI_WP_AUDIO_CFG			0x80 | ||||
| #define HDMI_WP_AUDIO_CFG2			0x84 | ||||
| #define HDMI_WP_AUDIO_CTRL			0x88 | ||||
| #define HDMI_WP_AUDIO_DATA			0x8C | ||||
| 
 | ||||
| /* HDMI WP IRQ flags */ | ||||
| #define HDMI_IRQ_CORE				(1 << 0) | ||||
| #define HDMI_IRQ_OCP_TIMEOUT			(1 << 4) | ||||
| #define HDMI_IRQ_AUDIO_FIFO_UNDERFLOW		(1 << 8) | ||||
| #define HDMI_IRQ_AUDIO_FIFO_OVERFLOW		(1 << 9) | ||||
| #define HDMI_IRQ_AUDIO_FIFO_SAMPLE_REQ		(1 << 10) | ||||
| #define HDMI_IRQ_VIDEO_VSYNC			(1 << 16) | ||||
| #define HDMI_IRQ_VIDEO_FRAME_DONE		(1 << 17) | ||||
| #define HDMI_IRQ_PHY_LINE5V_ASSERT		(1 << 24) | ||||
| #define HDMI_IRQ_LINK_CONNECT			(1 << 25) | ||||
| #define HDMI_IRQ_LINK_DISCONNECT		(1 << 26) | ||||
| #define HDMI_IRQ_PLL_LOCK			(1 << 29) | ||||
| #define HDMI_IRQ_PLL_UNLOCK			(1 << 30) | ||||
| #define HDMI_IRQ_PLL_RECAL			(1 << 31) | ||||
| 
 | ||||
| /* HDMI PLL */ | ||||
| 
 | ||||
| #define PLLCTRL_PLL_CONTROL			0x0 | ||||
| #define PLLCTRL_PLL_STATUS			0x4 | ||||
| #define PLLCTRL_PLL_GO				0x8 | ||||
| #define PLLCTRL_CFG1				0xC | ||||
| #define PLLCTRL_CFG2				0x10 | ||||
| #define PLLCTRL_CFG3				0x14 | ||||
| #define PLLCTRL_SSC_CFG1			0x18 | ||||
| #define PLLCTRL_SSC_CFG2			0x1C | ||||
| #define PLLCTRL_CFG4				0x20 | ||||
| 
 | ||||
| /* HDMI PHY */ | ||||
| 
 | ||||
| #define HDMI_TXPHY_TX_CTRL			0x0 | ||||
| #define HDMI_TXPHY_DIGITAL_CTRL			0x4 | ||||
| #define HDMI_TXPHY_POWER_CTRL			0x8 | ||||
| #define HDMI_TXPHY_PAD_CFG_CTRL			0xC | ||||
| #define HDMI_TXPHY_BIST_CONTROL			0x1C | ||||
| 
 | ||||
| enum hdmi_pll_pwr { | ||||
| 	HDMI_PLLPWRCMD_ALLOFF = 0, | ||||
| 	HDMI_PLLPWRCMD_PLLONLY = 1, | ||||
| 	HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2, | ||||
| 	HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_phy_pwr { | ||||
| 	HDMI_PHYPWRCMD_OFF = 0, | ||||
| 	HDMI_PHYPWRCMD_LDOON = 1, | ||||
| 	HDMI_PHYPWRCMD_TXON = 2 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_hdmi_dvi { | ||||
| 	HDMI_DVI = 0, | ||||
| 	HDMI_HDMI = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_clk_refsel { | ||||
| 	HDMI_REFSEL_PCLK = 0, | ||||
| 	HDMI_REFSEL_REF1 = 1, | ||||
| 	HDMI_REFSEL_REF2 = 2, | ||||
| 	HDMI_REFSEL_SYSCLK = 3 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_packing_mode { | ||||
| 	HDMI_PACK_10b_RGB_YUV444 = 0, | ||||
| 	HDMI_PACK_24b_RGB_YUV444_YUV422 = 1, | ||||
| 	HDMI_PACK_20b_YUV422 = 2, | ||||
| 	HDMI_PACK_ALREADYPACKED = 7 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_stereo_channels { | ||||
| 	HDMI_AUDIO_STEREO_NOCHANNELS = 0, | ||||
| 	HDMI_AUDIO_STEREO_ONECHANNEL = 1, | ||||
| 	HDMI_AUDIO_STEREO_TWOCHANNELS = 2, | ||||
| 	HDMI_AUDIO_STEREO_THREECHANNELS = 3, | ||||
| 	HDMI_AUDIO_STEREO_FOURCHANNELS = 4 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_type { | ||||
| 	HDMI_AUDIO_TYPE_LPCM = 0, | ||||
| 	HDMI_AUDIO_TYPE_IEC = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_justify { | ||||
| 	HDMI_AUDIO_JUSTIFY_LEFT = 0, | ||||
| 	HDMI_AUDIO_JUSTIFY_RIGHT = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_sample_order { | ||||
| 	HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0, | ||||
| 	HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_samples_perword { | ||||
| 	HDMI_AUDIO_ONEWORD_ONESAMPLE = 0, | ||||
| 	HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_sample_size_omap { | ||||
| 	HDMI_AUDIO_SAMPLE_16BITS = 0, | ||||
| 	HDMI_AUDIO_SAMPLE_24BITS = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_transf_mode { | ||||
| 	HDMI_AUDIO_TRANSF_DMA = 0, | ||||
| 	HDMI_AUDIO_TRANSF_IRQ = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_blk_strt_end_sig { | ||||
| 	HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0, | ||||
| 	HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_audio_layout { | ||||
| 	HDMI_AUDIO_LAYOUT_2CH = 0, | ||||
| 	HDMI_AUDIO_LAYOUT_8CH = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_cts_mode { | ||||
| 	HDMI_AUDIO_CTS_MODE_HW = 0, | ||||
| 	HDMI_AUDIO_CTS_MODE_SW = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_mclk_mode { | ||||
| 	HDMI_AUDIO_MCLK_128FS = 0, | ||||
| 	HDMI_AUDIO_MCLK_256FS = 1, | ||||
| 	HDMI_AUDIO_MCLK_384FS = 2, | ||||
| 	HDMI_AUDIO_MCLK_512FS = 3, | ||||
| 	HDMI_AUDIO_MCLK_768FS = 4, | ||||
| 	HDMI_AUDIO_MCLK_1024FS = 5, | ||||
| 	HDMI_AUDIO_MCLK_1152FS = 6, | ||||
| 	HDMI_AUDIO_MCLK_192FS = 7 | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_video_format { | ||||
| 	enum hdmi_packing_mode	packing_mode; | ||||
| 	u32			y_res;	/* Line per panel */ | ||||
| 	u32			x_res;	/* pixel per line */ | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_config { | ||||
| 	struct omap_video_timings timings; | ||||
| 	struct hdmi_avi_infoframe infoframe; | ||||
| 	enum hdmi_core_hdmi_dvi hdmi_dvi_mode; | ||||
| }; | ||||
| 
 | ||||
| /* HDMI PLL structure */ | ||||
| struct hdmi_pll_info { | ||||
| 	u16 regn; | ||||
| 	u16 regm; | ||||
| 	u32 regmf; | ||||
| 	u16 regm2; | ||||
| 	u16 regsd; | ||||
| 	u16 dcofreq; | ||||
| 	enum hdmi_clk_refsel refsel; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_audio_format { | ||||
| 	enum hdmi_stereo_channels		stereo_channels; | ||||
| 	u8					active_chnnls_msk; | ||||
| 	enum hdmi_audio_type			type; | ||||
| 	enum hdmi_audio_justify			justification; | ||||
| 	enum hdmi_audio_sample_order		sample_order; | ||||
| 	enum hdmi_audio_samples_perword		samples_per_word; | ||||
| 	enum hdmi_audio_sample_size_omap	sample_size; | ||||
| 	enum hdmi_audio_blk_strt_end_sig	en_sig_blk_strt_end; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_audio_dma { | ||||
| 	u8				transfer_size; | ||||
| 	u8				block_size; | ||||
| 	enum hdmi_audio_transf_mode	mode; | ||||
| 	u16				fifo_threshold; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_audio_i2s_config { | ||||
| 	u8 in_length_bits; | ||||
| 	u8 justification; | ||||
| 	u8 sck_edge_mode; | ||||
| 	u8 vbit; | ||||
| 	u8 direction; | ||||
| 	u8 shift; | ||||
| 	u8 active_sds; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_audio_config { | ||||
| 	struct hdmi_core_audio_i2s_config	i2s_cfg; | ||||
| 	struct snd_aes_iec958			*iec60958_cfg; | ||||
| 	bool					fs_override; | ||||
| 	u32					n; | ||||
| 	u32					cts; | ||||
| 	u32					aud_par_busclk; | ||||
| 	enum hdmi_core_audio_layout		layout; | ||||
| 	enum hdmi_core_cts_mode			cts_mode; | ||||
| 	bool					use_mclk; | ||||
| 	enum hdmi_audio_mclk_mode		mclk_mode; | ||||
| 	bool					en_acr_pkt; | ||||
| 	bool					en_dsd_audio; | ||||
| 	bool					en_parallel_aud_input; | ||||
| 	bool					en_spdif; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_wp_data { | ||||
| 	void __iomem *base; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_pll_data { | ||||
| 	void __iomem *base; | ||||
| 
 | ||||
| 	struct hdmi_pll_info info; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_phy_data { | ||||
| 	void __iomem *base; | ||||
| 
 | ||||
| 	u8 lane_function[4]; | ||||
| 	u8 lane_polarity[4]; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_data { | ||||
| 	void __iomem *base; | ||||
| }; | ||||
| 
 | ||||
| static inline void hdmi_write_reg(void __iomem *base_addr, const u32 idx, | ||||
| 		u32 val) | ||||
| { | ||||
| 	__raw_writel(val, base_addr + idx); | ||||
| } | ||||
| 
 | ||||
| static inline u32 hdmi_read_reg(void __iomem *base_addr, const u32 idx) | ||||
| { | ||||
| 	return __raw_readl(base_addr + idx); | ||||
| } | ||||
| 
 | ||||
| #define REG_FLD_MOD(base, idx, val, start, end) \ | ||||
| 	hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\ | ||||
| 							val, start, end)) | ||||
| #define REG_GET(base, idx, start, end) \ | ||||
| 	FLD_GET(hdmi_read_reg(base, idx), start, end) | ||||
| 
 | ||||
| static inline int hdmi_wait_for_bit_change(void __iomem *base_addr, | ||||
| 		const u32 idx, int b2, int b1, u32 val) | ||||
| { | ||||
| 	u32 t = 0, v; | ||||
| 	while (val != (v = REG_GET(base_addr, idx, b2, b1))) { | ||||
| 		if (t++ > 10000) | ||||
| 			return v; | ||||
| 		udelay(1); | ||||
| 	} | ||||
| 	return v; | ||||
| } | ||||
| 
 | ||||
| /* HDMI wrapper funcs */ | ||||
| int hdmi_wp_video_start(struct hdmi_wp_data *wp); | ||||
| void hdmi_wp_video_stop(struct hdmi_wp_data *wp); | ||||
| void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s); | ||||
| u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp); | ||||
| void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus); | ||||
| void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask); | ||||
| void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask); | ||||
| int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val); | ||||
| int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val); | ||||
| void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_video_format *video_fmt); | ||||
| void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, | ||||
| 		struct omap_video_timings *timings); | ||||
| void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, | ||||
| 		struct omap_video_timings *timings); | ||||
| void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, | ||||
| 		struct omap_video_timings *timings, struct hdmi_config *param); | ||||
| int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); | ||||
| 
 | ||||
| /* HDMI PLL funcs */ | ||||
| int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); | ||||
| void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); | ||||
| void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); | ||||
| void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy); | ||||
| int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); | ||||
| 
 | ||||
| /* HDMI PHY funcs */ | ||||
| int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg); | ||||
| void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); | ||||
| int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); | ||||
| int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); | ||||
| 
 | ||||
| /* HDMI common funcs */ | ||||
| int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep, | ||||
| 	struct hdmi_phy_data *phy); | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts); | ||||
| int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable); | ||||
| int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable); | ||||
| void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_audio_format *aud_fmt); | ||||
| void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_audio_dma *aud_dma); | ||||
| static inline bool hdmi_mode_has_audio(int mode) | ||||
| { | ||||
| 	return mode == HDMI_HDMI ? true : false; | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										796
									
								
								drivers/video/fbdev/omap2/dss/hdmi4.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										796
									
								
								drivers/video/fbdev/omap2/dss/hdmi4.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,796 @@ | |||
| /*
 | ||||
|  * HDMI interface DSS driver for TI's OMAP4 family of SoCs. | ||||
|  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
 | ||||
|  * Authors: Yong Zhi | ||||
|  *	Mythri pk <mythripk@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMI" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "hdmi4_core.h" | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static struct { | ||||
| 	struct mutex lock; | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	struct hdmi_wp_data	wp; | ||||
| 	struct hdmi_pll_data	pll; | ||||
| 	struct hdmi_phy_data	phy; | ||||
| 	struct hdmi_core_data	core; | ||||
| 
 | ||||
| 	struct hdmi_config cfg; | ||||
| 
 | ||||
| 	struct clk *sys_clk; | ||||
| 	struct regulator *vdda_hdmi_dac_reg; | ||||
| 
 | ||||
| 	bool core_enabled; | ||||
| 
 | ||||
| 	struct omap_dss_device output; | ||||
| } hdmi; | ||||
| 
 | ||||
| static int hdmi_runtime_get(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_runtime_get\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_get_sync(&hdmi.pdev->dev); | ||||
| 	WARN_ON(r < 0); | ||||
| 	if (r < 0) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_runtime_put(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_runtime_put\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_put_sync(&hdmi.pdev->dev); | ||||
| 	WARN_ON(r < 0 && r != -ENOSYS); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t hdmi_irq_handler(int irq, void *data) | ||||
| { | ||||
| 	struct hdmi_wp_data *wp = data; | ||||
| 	u32 irqstatus; | ||||
| 
 | ||||
| 	irqstatus = hdmi_wp_get_irqstatus(wp); | ||||
| 	hdmi_wp_set_irqstatus(wp, irqstatus); | ||||
| 
 | ||||
| 	if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && | ||||
| 			irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | ||||
| 		/*
 | ||||
| 		 * If we get both connect and disconnect interrupts at the same | ||||
| 		 * time, turn off the PHY, clear interrupts, and restart, which | ||||
| 		 * raises connect interrupt if a cable is connected, or nothing | ||||
| 		 * if cable is not connected. | ||||
| 		 */ | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); | ||||
| 
 | ||||
| 		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | | ||||
| 				HDMI_IRQ_LINK_DISCONNECT); | ||||
| 
 | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 	} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); | ||||
| 	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 	} | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_init_regulator(void) | ||||
| { | ||||
| 	int r; | ||||
| 	struct regulator *reg; | ||||
| 
 | ||||
| 	if (hdmi.vdda_hdmi_dac_reg != NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); | ||||
| 
 | ||||
| 	if (IS_ERR(reg)) { | ||||
| 		if (PTR_ERR(reg) != -EPROBE_DEFER) | ||||
| 			DSSERR("can't get VDDA regulator\n"); | ||||
| 		return PTR_ERR(reg); | ||||
| 	} | ||||
| 
 | ||||
| 	if (regulator_can_change_voltage(reg)) { | ||||
| 		r = regulator_set_voltage(reg, 1800000, 1800000); | ||||
| 		if (r) { | ||||
| 			devm_regulator_put(reg); | ||||
| 			DSSWARN("can't set the regulator voltage\n"); | ||||
| 			return r; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi.vdda_hdmi_dac_reg = reg; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_power_on_core(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = regulator_enable(hdmi.vdda_hdmi_dac_reg); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_runtime_get; | ||||
| 
 | ||||
| 	/* Make selection of HDMI in DSS */ | ||||
| 	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | ||||
| 
 | ||||
| 	hdmi.core_enabled = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_runtime_get: | ||||
| 	regulator_disable(hdmi.vdda_hdmi_dac_reg); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_power_off_core(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi.core_enabled = false; | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	regulator_disable(hdmi.vdda_hdmi_dac_reg); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_power_on_full(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 	struct omap_video_timings *p; | ||||
| 	struct omap_overlay_manager *mgr = hdmi.output.manager; | ||||
| 	unsigned long phy; | ||||
| 	struct hdmi_wp_data *wp = &hdmi.wp; | ||||
| 
 | ||||
| 	r = hdmi_power_on_core(dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	/* disable and clear irqs */ | ||||
| 	hdmi_wp_clear_irqenable(wp, 0xffffffff); | ||||
| 	hdmi_wp_set_irqstatus(wp, 0xffffffff); | ||||
| 
 | ||||
| 	p = &hdmi.cfg.timings; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | ||||
| 
 | ||||
| 	/* the functions below use kHz pixel clock. TODO: change to Hz */ | ||||
| 	phy = p->pixelclock / 1000; | ||||
| 
 | ||||
| 	hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); | ||||
| 
 | ||||
| 	/* config the PLL and PHY hdmi_set_pll_pwrfirst */ | ||||
| 	r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); | ||||
| 	if (r) { | ||||
| 		DSSDBG("Failed to lock PLL\n"); | ||||
| 		goto err_pll_enable; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | ||||
| 	if (r) { | ||||
| 		DSSDBG("Failed to configure PHY\n"); | ||||
| 		goto err_phy_cfg; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 	if (r) | ||||
| 		goto err_phy_pwr; | ||||
| 
 | ||||
| 	hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); | ||||
| 
 | ||||
| 	/* bypass TV gamma table */ | ||||
| 	dispc_enable_gamma_table(0); | ||||
| 
 | ||||
| 	/* tv size */ | ||||
| 	dss_mgr_set_timings(mgr, p); | ||||
| 
 | ||||
| 	r = hdmi_wp_video_start(&hdmi.wp); | ||||
| 	if (r) | ||||
| 		goto err_vid_enable; | ||||
| 
 | ||||
| 	r = dss_mgr_enable(mgr); | ||||
| 	if (r) | ||||
| 		goto err_mgr_enable; | ||||
| 
 | ||||
| 	hdmi_wp_set_irqenable(wp, | ||||
| 		HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_mgr_enable: | ||||
| 	hdmi_wp_video_stop(&hdmi.wp); | ||||
| err_vid_enable: | ||||
| err_phy_cfg: | ||||
| 	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | ||||
| err_phy_pwr: | ||||
| 	hdmi_pll_disable(&hdmi.pll, &hdmi.wp); | ||||
| err_pll_enable: | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_power_off_full(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = hdmi.output.manager; | ||||
| 
 | ||||
| 	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | ||||
| 
 | ||||
| 	dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 	hdmi_wp_video_stop(&hdmi.wp); | ||||
| 
 | ||||
| 	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | ||||
| 
 | ||||
| 	hdmi_pll_disable(&hdmi.pll, &hdmi.wp); | ||||
| 
 | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_display_check_timing(struct omap_dss_device *dssdev, | ||||
| 					struct omap_video_timings *timings) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_set_timing(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi.cfg.timings = *timings; | ||||
| 
 | ||||
| 	dispc_set_tv_pclk(timings->pixelclock); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	*timings = hdmi.cfg.timings; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_dump_regs(struct seq_file *s) | ||||
| { | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (hdmi_runtime_get()) { | ||||
| 		mutex_unlock(&hdmi.lock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi_wp_dump(&hdmi.wp, s); | ||||
| 	hdmi_pll_dump(&hdmi.pll, s); | ||||
| 	hdmi_phy_dump(&hdmi.phy, s); | ||||
| 	hdmi4_core_dump(&hdmi.core, s); | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int read_edid(u8 *buf, int len) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_runtime_get(); | ||||
| 	BUG_ON(r); | ||||
| 
 | ||||
| 	r = hdmi4_read_edid(&hdmi.core,  buf, len); | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_display_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	DSSDBG("ENTER hdmi_display_enable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (out == NULL || out->manager == NULL) { | ||||
| 		DSSERR("failed to enable display: no output/manager\n"); | ||||
| 		r = -ENODEV; | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_power_on_full(dssdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to power on device\n"); | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err0: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_display_disable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi_power_off_full(dssdev); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_core_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_power_on_core(dssdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to power on device\n"); | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err0: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	DSSDBG("Enter omapdss_hdmi_core_disable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_get_clocks(struct platform_device *pdev) | ||||
| { | ||||
| 	struct clk *clk; | ||||
| 
 | ||||
| 	clk = devm_clk_get(&pdev->dev, "sys_clk"); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		DSSERR("can't get sys_clk\n"); | ||||
| 		return PTR_ERR(clk); | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi.sys_clk = clk; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_connect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = hdmi_init_regulator(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||||
| 	if (!mgr) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = dss_mgr_connect(mgr, dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = omapdss_output_set_device(dssdev, dst); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to connect output to new device: %s\n", | ||||
| 				dst->name); | ||||
| 		dss_mgr_disconnect(mgr, dssdev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_disconnect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	WARN_ON(dst != dssdev->dst); | ||||
| 
 | ||||
| 	if (dst != dssdev->dst) | ||||
| 		return; | ||||
| 
 | ||||
| 	omapdss_output_unset_device(dssdev); | ||||
| 
 | ||||
| 	if (dssdev->manager) | ||||
| 		dss_mgr_disconnect(dssdev->manager, dssdev); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_read_edid(struct omap_dss_device *dssdev, | ||||
| 		u8 *edid, int len) | ||||
| { | ||||
| 	bool need_enable; | ||||
| 	int r; | ||||
| 
 | ||||
| 	need_enable = hdmi.core_enabled == false; | ||||
| 
 | ||||
| 	if (need_enable) { | ||||
| 		r = hdmi_core_enable(dssdev); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	r = read_edid(edid, len); | ||||
| 
 | ||||
| 	if (need_enable) | ||||
| 		hdmi_core_disable(dssdev); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||||
| static int hdmi_audio_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { | ||||
| 		r = -EPERM; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_audio_enable(&hdmi.wp, true); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi_wp_audio_enable(&hdmi.wp, false); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_start(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return hdmi4_audio_start(&hdmi.core, &hdmi.wp); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_stop(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi4_audio_stop(&hdmi.core, &hdmi.wp); | ||||
| } | ||||
| 
 | ||||
| static bool hdmi_audio_supported(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	bool r; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_config(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_audio *audio) | ||||
| { | ||||
| 	int r; | ||||
| 	u32 pclk = hdmi.cfg.timings.pixelclock; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { | ||||
| 		r = -EPERM; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| #else | ||||
| static int hdmi_audio_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_start(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_stop(struct omap_dss_device *dssdev) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static bool hdmi_audio_supported(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_config(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_audio *audio) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int hdmi_set_infoframe(struct omap_dss_device *dssdev, | ||||
| 		const struct hdmi_avi_infoframe *avi) | ||||
| { | ||||
| 	hdmi.cfg.infoframe = *avi; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, | ||||
| 		bool hdmi_mode) | ||||
| { | ||||
| 	hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct omapdss_hdmi_ops hdmi_ops = { | ||||
| 	.connect		= hdmi_connect, | ||||
| 	.disconnect		= hdmi_disconnect, | ||||
| 
 | ||||
| 	.enable			= hdmi_display_enable, | ||||
| 	.disable		= hdmi_display_disable, | ||||
| 
 | ||||
| 	.check_timings		= hdmi_display_check_timing, | ||||
| 	.set_timings		= hdmi_display_set_timing, | ||||
| 	.get_timings		= hdmi_display_get_timings, | ||||
| 
 | ||||
| 	.read_edid		= hdmi_read_edid, | ||||
| 	.set_infoframe		= hdmi_set_infoframe, | ||||
| 	.set_hdmi_mode		= hdmi_set_hdmi_mode, | ||||
| 
 | ||||
| 	.audio_enable		= hdmi_audio_enable, | ||||
| 	.audio_disable		= hdmi_audio_disable, | ||||
| 	.audio_start		= hdmi_audio_start, | ||||
| 	.audio_stop		= hdmi_audio_stop, | ||||
| 	.audio_supported	= hdmi_audio_supported, | ||||
| 	.audio_config		= hdmi_audio_config, | ||||
| }; | ||||
| 
 | ||||
| static void hdmi_init_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	out->dev = &pdev->dev; | ||||
| 	out->id = OMAP_DSS_OUTPUT_HDMI; | ||||
| 	out->output_type = OMAP_DISPLAY_TYPE_HDMI; | ||||
| 	out->name = "hdmi.0"; | ||||
| 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | ||||
| 	out->ops.hdmi = &hdmi_ops; | ||||
| 	out->owner = THIS_MODULE; | ||||
| 
 | ||||
| 	omapdss_register_output(out); | ||||
| } | ||||
| 
 | ||||
| static void __exit hdmi_uninit_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	omapdss_unregister_output(out); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_probe_of(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *node = pdev->dev.of_node; | ||||
| 	struct device_node *ep; | ||||
| 	int r; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_first_endpoint(node); | ||||
| 	if (!ep) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	of_node_put(ep); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| /* HDMI HW IP initialisation */ | ||||
| static int omapdss_hdmihw_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int r; | ||||
| 	int irq; | ||||
| 
 | ||||
| 	hdmi.pdev = pdev; | ||||
| 
 | ||||
| 	mutex_init(&hdmi.lock); | ||||
| 
 | ||||
| 	if (pdev->dev.of_node) { | ||||
| 		r = hdmi_probe_of(pdev); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_init(pdev, &hdmi.wp); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_pll_init(pdev, &hdmi.pll); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_phy_init(pdev, &hdmi.phy); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi4_core_init(pdev, &hdmi.core); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_get_clocks(pdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("can't get clocks\n"); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	irq = platform_get_irq(pdev, 0); | ||||
| 	if (irq < 0) { | ||||
| 		DSSERR("platform_get_irq failed\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	r = devm_request_threaded_irq(&pdev->dev, irq, | ||||
| 			NULL, hdmi_irq_handler, | ||||
| 			IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); | ||||
| 	if (r) { | ||||
| 		DSSERR("HDMI IRQ request failed\n"); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	pm_runtime_enable(&pdev->dev); | ||||
| 
 | ||||
| 	hdmi_init_output(pdev); | ||||
| 
 | ||||
| 	dss_debugfs_create_file("hdmi", hdmi_dump_regs); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	hdmi_uninit_output(pdev); | ||||
| 
 | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_runtime_suspend(struct device *dev) | ||||
| { | ||||
| 	clk_disable_unprepare(hdmi.sys_clk); | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_runtime_resume(struct device *dev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r < 0) | ||||
| 		return r; | ||||
| 
 | ||||
| 	clk_prepare_enable(hdmi.sys_clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops hdmi_pm_ops = { | ||||
| 	.runtime_suspend = hdmi_runtime_suspend, | ||||
| 	.runtime_resume = hdmi_runtime_resume, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id hdmi_of_match[] = { | ||||
| 	{ .compatible = "ti,omap4-hdmi", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver omapdss_hdmihw_driver = { | ||||
| 	.probe		= omapdss_hdmihw_probe, | ||||
| 	.remove         = __exit_p(omapdss_hdmihw_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_hdmi", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.pm	= &hdmi_pm_ops, | ||||
| 		.of_match_table = hdmi_of_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init hdmi4_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&omapdss_hdmihw_driver); | ||||
| } | ||||
| 
 | ||||
| void __exit hdmi4_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omapdss_hdmihw_driver); | ||||
| } | ||||
							
								
								
									
										908
									
								
								drivers/video/fbdev/omap2/dss/hdmi4_core.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										908
									
								
								drivers/video/fbdev/omap2/dss/hdmi4_core.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,908 @@ | |||
| /*
 | ||||
|  * ti_hdmi_4xxx_ip.c | ||||
|  * | ||||
|  * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library | ||||
|  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
 | ||||
|  * Authors: Yong Zhi | ||||
|  *	Mythri pk <mythripk@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMICORE" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/seq_file.h> | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||||
| #include <sound/asound.h> | ||||
| #include <sound/asoundef.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "hdmi4_core.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| #define HDMI_CORE_AV		0x500 | ||||
| 
 | ||||
| static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core) | ||||
| { | ||||
| 	return core->base + HDMI_CORE_AV; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_core_ddc_init(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	/* Turn on CLK for DDC */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0); | ||||
| 
 | ||||
| 	/* IN_PROG */ | ||||
| 	if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 1) { | ||||
| 		/* Abort transaction */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xf, 3, 0); | ||||
| 		/* IN_PROG */ | ||||
| 		if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, | ||||
| 					4, 4, 0) != 0) { | ||||
| 			DSSERR("Timeout aborting DDC transaction\n"); | ||||
| 			return -ETIMEDOUT; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clk SCL Devices */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0xA, 3, 0); | ||||
| 
 | ||||
| 	/* HDMI_CORE_DDC_STATUS_IN_PROG */ | ||||
| 	if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, | ||||
| 				4, 4, 0) != 0) { | ||||
| 		DSSERR("Timeout starting SCL clock\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Clear FIFO */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x9, 3, 0); | ||||
| 
 | ||||
| 	/* HDMI_CORE_DDC_STATUS_IN_PROG */ | ||||
| 	if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, | ||||
| 				4, 4, 0) != 0) { | ||||
| 		DSSERR("Timeout clearing DDC fifo\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_core_ddc_edid(struct hdmi_core_data *core, | ||||
| 		u8 *pedid, int ext) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	u32 i; | ||||
| 	char checksum; | ||||
| 	u32 offset = 0; | ||||
| 
 | ||||
| 	/* HDMI_CORE_DDC_STATUS_IN_PROG */ | ||||
| 	if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, | ||||
| 				4, 4, 0) != 0) { | ||||
| 		DSSERR("Timeout waiting DDC to be ready\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ext % 2 != 0) | ||||
| 		offset = 0x80; | ||||
| 
 | ||||
| 	/* Load Segment Address Register */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0); | ||||
| 
 | ||||
| 	/* Load Slave Address Register */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); | ||||
| 
 | ||||
| 	/* Load Offset Address Register */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0); | ||||
| 
 | ||||
| 	/* Load Byte Count */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); | ||||
| 
 | ||||
| 	/* Set DDC_CMD */ | ||||
| 	if (ext) | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0); | ||||
| 	else | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0); | ||||
| 
 | ||||
| 	/* HDMI_CORE_DDC_STATUS_BUS_LOW */ | ||||
| 	if (REG_GET(base, HDMI_CORE_DDC_STATUS, 6, 6) == 1) { | ||||
| 		DSSERR("I2C Bus Low?\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 	/* HDMI_CORE_DDC_STATUS_NO_ACK */ | ||||
| 	if (REG_GET(base, HDMI_CORE_DDC_STATUS, 5, 5) == 1) { | ||||
| 		DSSERR("I2C No Ack\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < 0x80; ++i) { | ||||
| 		int t; | ||||
| 
 | ||||
| 		/* IN_PROG */ | ||||
| 		if (REG_GET(base, HDMI_CORE_DDC_STATUS, 4, 4) == 0) { | ||||
| 			DSSERR("operation stopped when reading edid\n"); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 
 | ||||
| 		t = 0; | ||||
| 		/* FIFO_EMPTY */ | ||||
| 		while (REG_GET(base, HDMI_CORE_DDC_STATUS, 2, 2) == 1) { | ||||
| 			if (t++ > 10000) { | ||||
| 				DSSERR("timeout reading edid\n"); | ||||
| 				return -ETIMEDOUT; | ||||
| 			} | ||||
| 			udelay(1); | ||||
| 		} | ||||
| 
 | ||||
| 		pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	checksum = 0; | ||||
| 	for (i = 0; i < 0x80; ++i) | ||||
| 		checksum += pedid[i]; | ||||
| 
 | ||||
| 	if (checksum != 0) { | ||||
| 		DSSERR("E-EDID checksum failed!!\n"); | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len) | ||||
| { | ||||
| 	int r, l; | ||||
| 
 | ||||
| 	if (len < 128) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	r = hdmi_core_ddc_init(core); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_core_ddc_edid(core, edid, 0); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	l = 128; | ||||
| 
 | ||||
| 	if (len >= 128 * 2 && edid[0x7e] > 0) { | ||||
| 		r = hdmi_core_ddc_edid(core, edid + 0x80, 1); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 		l += 128; | ||||
| 	} | ||||
| 
 | ||||
| 	return l; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_init(struct hdmi_core_video_config *video_cfg) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_core_init\n"); | ||||
| 
 | ||||
| 	/* video core */ | ||||
| 	video_cfg->ip_bus_width = HDMI_INPUT_8BIT; | ||||
| 	video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT; | ||||
| 	video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE; | ||||
| 	video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE; | ||||
| 	video_cfg->hdmi_dvi = HDMI_DVI; | ||||
| 	video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_powerdown_disable(struct hdmi_core_data *core) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_core_powerdown_disable\n"); | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_SYS_SYS_CTRL1, 0x0, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_swreset_release(struct hdmi_core_data *core) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_core_swreset_release\n"); | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x0, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_swreset_assert(struct hdmi_core_data *core) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_core_swreset_assert\n"); | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x1, 0, 0); | ||||
| } | ||||
| 
 | ||||
| /* HDMI_CORE_VIDEO_CONFIG */ | ||||
| static void hdmi_core_video_config(struct hdmi_core_data *core, | ||||
| 				struct hdmi_core_video_config *cfg) | ||||
| { | ||||
| 	u32 r = 0; | ||||
| 	void __iomem *core_sys_base = core->base; | ||||
| 	void __iomem *core_av_base = hdmi_av_base(core); | ||||
| 
 | ||||
| 	/* sys_ctrl1 default configuration not tunable */ | ||||
| 	r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1); | ||||
| 	r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC, 5, 5); | ||||
| 	r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC, 4, 4); | ||||
| 	r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS, 2, 2); | ||||
| 	r = FLD_MOD(r, HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE, 1, 1); | ||||
| 	hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1, r); | ||||
| 
 | ||||
| 	REG_FLD_MOD(core_sys_base, | ||||
| 			HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6); | ||||
| 
 | ||||
| 	/* Vid_Mode */ | ||||
| 	r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE); | ||||
| 
 | ||||
| 	/* dither truncation configuration */ | ||||
| 	if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) { | ||||
| 		r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6); | ||||
| 		r = FLD_MOD(r, 1, 5, 5); | ||||
| 	} else { | ||||
| 		r = FLD_MOD(r, cfg->op_dither_truc, 7, 6); | ||||
| 		r = FLD_MOD(r, 0, 5, 5); | ||||
| 	} | ||||
| 	hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r); | ||||
| 
 | ||||
| 	/* HDMI_Ctrl */ | ||||
| 	r = hdmi_read_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL); | ||||
| 	r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6); | ||||
| 	r = FLD_MOD(r, cfg->pkt_mode, 5, 3); | ||||
| 	r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0); | ||||
| 	hdmi_write_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL, r); | ||||
| 
 | ||||
| 	/* TMDS_CTRL */ | ||||
| 	REG_FLD_MOD(core_sys_base, | ||||
| 			HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core, | ||||
| 	struct hdmi_avi_infoframe *frame) | ||||
| { | ||||
| 	void __iomem *av_base = hdmi_av_base(core); | ||||
| 	u8 data[HDMI_INFOFRAME_SIZE(AVI)]; | ||||
| 	int i; | ||||
| 
 | ||||
| 	hdmi_avi_infoframe_pack(frame, data, sizeof(data)); | ||||
| 
 | ||||
| 	print_hex_dump_debug("AVI: ", DUMP_PREFIX_NONE, 16, 1, data, | ||||
| 		HDMI_INFOFRAME_SIZE(AVI), false); | ||||
| 
 | ||||
| 	for (i = 0; i < sizeof(data); ++i) { | ||||
| 		hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_BASE + i * 4, | ||||
| 			data[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_av_packet_config(struct hdmi_core_data *core, | ||||
| 		struct hdmi_core_packet_enable_repeat repeat_cfg) | ||||
| { | ||||
| 	/* enable/repeat the infoframe */ | ||||
| 	hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL1, | ||||
| 		(repeat_cfg.audio_pkt << 5) | | ||||
| 		(repeat_cfg.audio_pkt_repeat << 4) | | ||||
| 		(repeat_cfg.avi_infoframe << 1) | | ||||
| 		(repeat_cfg.avi_infoframe_repeat)); | ||||
| 
 | ||||
| 	/* enable/repeat the packet */ | ||||
| 	hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL2, | ||||
| 		(repeat_cfg.gen_cntrl_pkt << 3) | | ||||
| 		(repeat_cfg.gen_cntrl_pkt_repeat << 2) | | ||||
| 		(repeat_cfg.generic_pkt << 1) | | ||||
| 		(repeat_cfg.generic_pkt_repeat)); | ||||
| } | ||||
| 
 | ||||
| void hdmi4_configure(struct hdmi_core_data *core, | ||||
| 	struct hdmi_wp_data *wp, struct hdmi_config *cfg) | ||||
| { | ||||
| 	/* HDMI */ | ||||
| 	struct omap_video_timings video_timing; | ||||
| 	struct hdmi_video_format video_format; | ||||
| 	/* HDMI core */ | ||||
| 	struct hdmi_core_video_config v_core_cfg; | ||||
| 	struct hdmi_core_packet_enable_repeat repeat_cfg = { 0 }; | ||||
| 
 | ||||
| 	hdmi_core_init(&v_core_cfg); | ||||
| 
 | ||||
| 	hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg); | ||||
| 
 | ||||
| 	hdmi_wp_video_config_timing(wp, &video_timing); | ||||
| 
 | ||||
| 	/* video config */ | ||||
| 	video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422; | ||||
| 
 | ||||
| 	hdmi_wp_video_config_format(wp, &video_format); | ||||
| 
 | ||||
| 	hdmi_wp_video_config_interface(wp, &video_timing); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * configure core video part | ||||
| 	 * set software reset in the core | ||||
| 	 */ | ||||
| 	hdmi_core_swreset_assert(core); | ||||
| 
 | ||||
| 	/* power down off */ | ||||
| 	hdmi_core_powerdown_disable(core); | ||||
| 
 | ||||
| 	v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL; | ||||
| 	v_core_cfg.hdmi_dvi = cfg->hdmi_dvi_mode; | ||||
| 
 | ||||
| 	hdmi_core_video_config(core, &v_core_cfg); | ||||
| 
 | ||||
| 	/* release software reset in the core */ | ||||
| 	hdmi_core_swreset_release(core); | ||||
| 
 | ||||
| 	if (cfg->hdmi_dvi_mode == HDMI_HDMI) { | ||||
| 		hdmi_core_write_avi_infoframe(core, &cfg->infoframe); | ||||
| 
 | ||||
| 		/* enable/repeat the infoframe */ | ||||
| 		repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; | ||||
| 		repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON; | ||||
| 		/* wakeup */ | ||||
| 		repeat_cfg.audio_pkt = HDMI_PACKETENABLE; | ||||
| 		repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON; | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi_core_av_packet_config(core, repeat_cfg); | ||||
| } | ||||
| 
 | ||||
| void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| #define CORE_REG(i, name) name(i) | ||||
| #define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ | ||||
| 		hdmi_read_reg(core->base, r)) | ||||
| #define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\ | ||||
| 		hdmi_read_reg(hdmi_av_base(core), r)) | ||||
| #define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ | ||||
| 		(i < 10) ? 32 - (int)strlen(#r) : 31 - (int)strlen(#r), " ", \ | ||||
| 		hdmi_read_reg(hdmi_av_base(core), CORE_REG(i, r))) | ||||
| 
 | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VND_IDL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DEV_IDL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DEV_IDH); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DEV_REV); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_SRST); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_SYS_CTRL1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_SYS_STAT); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_SYS_CTRL3); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_DLY); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_CTRL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_TOP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_CNTL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_CNTH); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_LINL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_HRES_L); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_HRES_H); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VRES_L); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VRES_H); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_IADJUST); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_POLDETECT); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_HWIDTH1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_HWIDTH2); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VWIDTH); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_CTRL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_ACEN); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_MODE); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_BLANK1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_BLANK3); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_BLANK1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_DC_HEADER); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_VID_DITHER); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_RGB2XVYCC_CT); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2Y_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2Y_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2Y_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2CB_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2CB_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2CB_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_R2CR_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_G2CR_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_B2CR_COEFF_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_RGB_OFFSET_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_Y_OFFSET_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_LOW); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_CBCR_OFFSET_UP); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_STATE); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR2); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR3); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR4); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK1); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK2); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK3); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_UNMASK4); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_INTR_CTRL); | ||||
| 	DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL); | ||||
| 
 | ||||
| 	DUMPCORE(HDMI_CORE_DDC_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_SEGM); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_OFFSET); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_COUNT1); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_COUNT2); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_STATUS); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_CMD); | ||||
| 	DUMPCORE(HDMI_CORE_DDC_DATA); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_ACR_CTRL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_FREQ_SVAL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_N_SVAL1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_N_SVAL2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_N_SVAL3); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL3); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL3); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUD_MODE); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPDIF_CTRL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_HW_SPDIF_FS); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SWAP_I2S); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPDIF_ERTH); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_IN_MAP); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_IN_CTRL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_CHST0); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_CHST1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_CHST2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_CHST4); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_CHST5); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_ASRC); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_I2S_IN_LEN); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_HDMI_CTRL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUDO_TXSTAT); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_TEST_TXCTRL); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_DPD); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_PB_CTRL1); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_PB_CTRL2); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AVI_TYPE); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AVI_VERS); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AVI_LEN); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AVI_CHSUM); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_AVI_DBYTE); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPD_TYPE); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPD_VERS); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPD_LEN); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_SPD_CHSUM); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_SPD_DBYTE); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUDIO_TYPE); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUDIO_VERS); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUDIO_LEN); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_AUDIO_CHSUM); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_AUD_DBYTE); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_MPEG_TYPE); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_MPEG_VERS); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_MPEG_LEN); | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_MPEG_CHSUM); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_MPEG_DBYTE); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_GEN_DBYTE); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CP_BYTE1); | ||||
| 
 | ||||
| 	for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++) | ||||
| 		DUMPCOREAV2(i, HDMI_CORE_AV_GEN2_DBYTE); | ||||
| 
 | ||||
| 	DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||||
| static void hdmi_core_audio_config(struct hdmi_core_data *core, | ||||
| 					struct hdmi_core_audio_config *cfg) | ||||
| { | ||||
| 	u32 r; | ||||
| 	void __iomem *av_base = hdmi_av_base(core); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Parameters for generation of Audio Clock Recovery packets | ||||
| 	 */ | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); | ||||
| 
 | ||||
| 	if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) { | ||||
| 		REG_FLD_MOD(av_base, HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0); | ||||
| 		REG_FLD_MOD(av_base, | ||||
| 				HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0); | ||||
| 		REG_FLD_MOD(av_base, | ||||
| 				HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); | ||||
| 	} else { | ||||
| 		REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1, | ||||
| 				cfg->aud_par_busclk, 7, 0); | ||||
| 		REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2, | ||||
| 				(cfg->aud_par_busclk >> 8), 7, 0); | ||||
| 		REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_3, | ||||
| 				(cfg->aud_par_busclk >> 16), 7, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set ACR clock divisor */ | ||||
| 	REG_FLD_MOD(av_base, | ||||
| 			HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL); | ||||
| 	/*
 | ||||
| 	 * Use TMDS clock for ACR packets. For devices that use | ||||
| 	 * the MCLK, this is the first part of the MCLK initialization. | ||||
| 	 */ | ||||
| 	r = FLD_MOD(r, 0, 2, 2); | ||||
| 
 | ||||
| 	r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); | ||||
| 	r = FLD_MOD(r, cfg->cts_mode, 0, 0); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r); | ||||
| 
 | ||||
| 	/* For devices using MCLK, this completes its initialization. */ | ||||
| 	if (cfg->use_mclk) | ||||
| 		REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2); | ||||
| 
 | ||||
| 	/* Override of SPDIF sample frequency with value in I2S_CHST4 */ | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, | ||||
| 						cfg->fs_override, 1, 1); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set IEC-60958-3 channel status word. It is passed to the IP | ||||
| 	 * just as it is received. The user of the driver is responsible | ||||
| 	 * for its contents. | ||||
| 	 */ | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0, | ||||
| 		       cfg->iec60958_cfg->status[0]); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1, | ||||
| 		       cfg->iec60958_cfg->status[1]); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2, | ||||
| 		       cfg->iec60958_cfg->status[2]); | ||||
| 	/* yes, this is correct: status[3] goes to CHST4 register */ | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4, | ||||
| 		       cfg->iec60958_cfg->status[3]); | ||||
| 	/* yes, this is correct: status[4] goes to CHST5 register */ | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, | ||||
| 		       cfg->iec60958_cfg->status[4]); | ||||
| 
 | ||||
| 	/* set I2S parameters */ | ||||
| 	r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r); | ||||
| 
 | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, | ||||
| 			cfg->i2s_cfg.in_length_bits, 3, 0); | ||||
| 
 | ||||
| 	/* Audio channels and mode parameters */ | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1); | ||||
| 	r = hdmi_read_reg(av_base, HDMI_CORE_AV_AUD_MODE); | ||||
| 	r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4); | ||||
| 	r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3); | ||||
| 	r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); | ||||
| 	r = FLD_MOD(r, cfg->en_spdif, 1, 1); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); | ||||
| 
 | ||||
| 	/* Audio channel mappings */ | ||||
| 	/* TODO: Make channel mapping dynamic. For now, map channels
 | ||||
| 	 * in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as | ||||
| 	 * HDMI speaker order is different. See CEA-861 Section 6.6.2. | ||||
| 	 */ | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78); | ||||
| 	REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core, | ||||
| 		struct snd_cea_861_aud_if *info_aud) | ||||
| { | ||||
| 	u8 sum = 0, checksum = 0; | ||||
| 	void __iomem *av_base = hdmi_av_base(core); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set audio info frame type, version and length as | ||||
| 	 * described in HDMI 1.4a Section 8.2.2 specification. | ||||
| 	 * Checksum calculation is defined in Section 5.3.5. | ||||
| 	 */ | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_TYPE, 0x84); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_VERS, 0x01); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); | ||||
| 	sum += 0x84 + 0x001 + 0x00a; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), | ||||
| 		       info_aud->db1_ct_cc); | ||||
| 	sum += info_aud->db1_ct_cc; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), | ||||
| 		       info_aud->db2_sf_ss); | ||||
| 	sum += info_aud->db2_sf_ss; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3); | ||||
| 	sum += info_aud->db3; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca); | ||||
| 	sum += info_aud->db4_ca; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), | ||||
| 		       info_aud->db5_dminh_lsv); | ||||
| 	sum += info_aud->db5_dminh_lsv; | ||||
| 
 | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(7), 0x00); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(8), 0x00); | ||||
| 	hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(9), 0x00); | ||||
| 
 | ||||
| 	checksum = 0x100 - sum; | ||||
| 	hdmi_write_reg(av_base, | ||||
| 					HDMI_CORE_AV_AUDIO_CHSUM, checksum); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing | ||||
| 	 * is available. | ||||
| 	 */ | ||||
| } | ||||
| 
 | ||||
| int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 		struct omap_dss_audio *audio, u32 pclk) | ||||
| { | ||||
| 	struct hdmi_audio_format audio_format; | ||||
| 	struct hdmi_audio_dma audio_dma; | ||||
| 	struct hdmi_core_audio_config acore; | ||||
| 	int err, n, cts, channel_count; | ||||
| 	unsigned int fs_nr; | ||||
| 	bool word_length_16b = false; | ||||
| 
 | ||||
| 	if (!audio || !audio->iec || !audio->cea || !core) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	acore.iec60958_cfg = audio->iec; | ||||
| 	/*
 | ||||
| 	 * In the IEC-60958 status word, check if the audio sample word length | ||||
| 	 * is 16-bit as several optimizations can be performed in such case. | ||||
| 	 */ | ||||
| 	if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)) | ||||
| 		if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16) | ||||
| 			word_length_16b = true; | ||||
| 
 | ||||
| 	/* I2S configuration. See Phillips' specification */ | ||||
| 	if (word_length_16b) | ||||
| 		acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; | ||||
| 	else | ||||
| 		acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; | ||||
| 	/*
 | ||||
| 	 * The I2S input word length is twice the lenght given in the IEC-60958 | ||||
| 	 * status word. If the word size is greater than | ||||
| 	 * 20 bits, increment by one. | ||||
| 	 */ | ||||
| 	acore.i2s_cfg.in_length_bits = audio->iec->status[4] | ||||
| 		& IEC958_AES4_CON_WORDLEN; | ||||
| 	if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) | ||||
| 		acore.i2s_cfg.in_length_bits++; | ||||
| 	acore.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; | ||||
| 	acore.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; | ||||
| 	acore.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; | ||||
| 	acore.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; | ||||
| 
 | ||||
| 	/* convert sample frequency to a number */ | ||||
| 	switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { | ||||
| 	case IEC958_AES3_CON_FS_32000: | ||||
| 		fs_nr = 32000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_44100: | ||||
| 		fs_nr = 44100; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_48000: | ||||
| 		fs_nr = 48000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_88200: | ||||
| 		fs_nr = 88200; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_96000: | ||||
| 		fs_nr = 96000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_176400: | ||||
| 		fs_nr = 176400; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_192000: | ||||
| 		fs_nr = 192000; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	err = hdmi_compute_acr(pclk, fs_nr, &n, &cts); | ||||
| 
 | ||||
| 	/* Audio clock regeneration settings */ | ||||
| 	acore.n = n; | ||||
| 	acore.cts = cts; | ||||
| 	if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { | ||||
| 		acore.aud_par_busclk = 0; | ||||
| 		acore.cts_mode = HDMI_AUDIO_CTS_MODE_SW; | ||||
| 		acore.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); | ||||
| 	} else { | ||||
| 		acore.aud_par_busclk = (((128 * 31) - 1) << 8); | ||||
| 		acore.cts_mode = HDMI_AUDIO_CTS_MODE_HW; | ||||
| 		acore.use_mclk = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (acore.use_mclk) | ||||
| 		acore.mclk_mode = HDMI_AUDIO_MCLK_128FS; | ||||
| 
 | ||||
| 	/* Audio channels settings */ | ||||
| 	channel_count = (audio->cea->db1_ct_cc & | ||||
| 			 CEA861_AUDIO_INFOFRAME_DB1CC) + 1; | ||||
| 
 | ||||
| 	switch (channel_count) { | ||||
| 	case 2: | ||||
| 		audio_format.active_chnnls_msk = 0x03; | ||||
| 		break; | ||||
| 	case 3: | ||||
| 		audio_format.active_chnnls_msk = 0x07; | ||||
| 		break; | ||||
| 	case 4: | ||||
| 		audio_format.active_chnnls_msk = 0x0f; | ||||
| 		break; | ||||
| 	case 5: | ||||
| 		audio_format.active_chnnls_msk = 0x1f; | ||||
| 		break; | ||||
| 	case 6: | ||||
| 		audio_format.active_chnnls_msk = 0x3f; | ||||
| 		break; | ||||
| 	case 7: | ||||
| 		audio_format.active_chnnls_msk = 0x7f; | ||||
| 		break; | ||||
| 	case 8: | ||||
| 		audio_format.active_chnnls_msk = 0xff; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * the HDMI IP needs to enable four stereo channels when transmitting | ||||
| 	 * more than 2 audio channels | ||||
| 	 */ | ||||
| 	if (channel_count == 2) { | ||||
| 		audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; | ||||
| 		acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; | ||||
| 		acore.layout = HDMI_AUDIO_LAYOUT_2CH; | ||||
| 	} else { | ||||
| 		audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS; | ||||
| 		acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN | | ||||
| 				HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN | | ||||
| 				HDMI_AUDIO_I2S_SD3_EN; | ||||
| 		acore.layout = HDMI_AUDIO_LAYOUT_8CH; | ||||
| 	} | ||||
| 
 | ||||
| 	acore.en_spdif = false; | ||||
| 	/* use sample frequency from channel status word */ | ||||
| 	acore.fs_override = true; | ||||
| 	/* enable ACR packets */ | ||||
| 	acore.en_acr_pkt = true; | ||||
| 	/* disable direct streaming digital audio */ | ||||
| 	acore.en_dsd_audio = false; | ||||
| 	/* use parallel audio interface */ | ||||
| 	acore.en_parallel_aud_input = true; | ||||
| 
 | ||||
| 	/* DMA settings */ | ||||
| 	if (word_length_16b) | ||||
| 		audio_dma.transfer_size = 0x10; | ||||
| 	else | ||||
| 		audio_dma.transfer_size = 0x20; | ||||
| 	audio_dma.block_size = 0xC0; | ||||
| 	audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; | ||||
| 	audio_dma.fifo_threshold = 0x20; /* in number of samples */ | ||||
| 
 | ||||
| 	/* audio FIFO format settings */ | ||||
| 	if (word_length_16b) { | ||||
| 		audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; | ||||
| 		audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; | ||||
| 		audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; | ||||
| 	} else { | ||||
| 		audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; | ||||
| 		audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; | ||||
| 		audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; | ||||
| 	} | ||||
| 	audio_format.type = HDMI_AUDIO_TYPE_LPCM; | ||||
| 	audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; | ||||
| 	/* disable start/stop signals of IEC 60958 blocks */ | ||||
| 	audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; | ||||
| 
 | ||||
| 	/* configure DMA and audio FIFO format*/ | ||||
| 	hdmi_wp_audio_config_dma(wp, &audio_dma); | ||||
| 	hdmi_wp_audio_config_format(wp, &audio_format); | ||||
| 
 | ||||
| 	/* configure the core*/ | ||||
| 	hdmi_core_audio_config(core, &acore); | ||||
| 
 | ||||
| 	/* configure CEA 861 audio infoframe*/ | ||||
| 	hdmi_core_audio_infoframe_cfg(core, audio->cea); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	REG_FLD_MOD(hdmi_av_base(core), | ||||
| 		    HDMI_CORE_AV_AUD_MODE, true, 0, 0); | ||||
| 
 | ||||
| 	hdmi_wp_audio_core_req_enable(wp, true); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	REG_FLD_MOD(hdmi_av_base(core), | ||||
| 		    HDMI_CORE_AV_AUD_MODE, false, 0, 0); | ||||
| 
 | ||||
| 	hdmi_wp_audio_core_req_enable(wp, false); | ||||
| } | ||||
| 
 | ||||
| int hdmi4_audio_get_dma_port(u32 *offset, u32 *size) | ||||
| { | ||||
| 	if (!offset || !size) | ||||
| 		return -EINVAL; | ||||
| 	*offset = HDMI_WP_AUDIO_DATA; | ||||
| 	*size = 4; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | ||||
| 	if (!res) { | ||||
| 		DSSERR("can't get CORE mem resource\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	core->base = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(core->base)) { | ||||
| 		DSSERR("can't ioremap CORE\n"); | ||||
| 		return PTR_ERR(core->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										277
									
								
								drivers/video/fbdev/omap2/dss/hdmi4_core.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								drivers/video/fbdev/omap2/dss/hdmi4_core.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,277 @@ | |||
| /*
 | ||||
|  * HDMI header definition for OMAP4 HDMI core IP | ||||
|  * | ||||
|  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
 | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _HDMI4_CORE_H_ | ||||
| #define _HDMI4_CORE_H_ | ||||
| 
 | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| /* OMAP4 HDMI IP Core System */ | ||||
| 
 | ||||
| #define HDMI_CORE_SYS_VND_IDL			0x0 | ||||
| #define HDMI_CORE_SYS_DEV_IDL			0x8 | ||||
| #define HDMI_CORE_SYS_DEV_IDH			0xC | ||||
| #define HDMI_CORE_SYS_DEV_REV			0x10 | ||||
| #define HDMI_CORE_SYS_SRST			0x14 | ||||
| #define HDMI_CORE_SYS_SYS_CTRL1			0x20 | ||||
| #define HDMI_CORE_SYS_SYS_STAT			0x24 | ||||
| #define HDMI_CORE_SYS_SYS_CTRL3			0x28 | ||||
| #define HDMI_CORE_SYS_DCTL			0x34 | ||||
| #define HDMI_CORE_SYS_DE_DLY			0xC8 | ||||
| #define HDMI_CORE_SYS_DE_CTRL			0xCC | ||||
| #define HDMI_CORE_SYS_DE_TOP			0xD0 | ||||
| #define HDMI_CORE_SYS_DE_CNTL			0xD8 | ||||
| #define HDMI_CORE_SYS_DE_CNTH			0xDC | ||||
| #define HDMI_CORE_SYS_DE_LINL			0xE0 | ||||
| #define HDMI_CORE_SYS_DE_LINH_1			0xE4 | ||||
| #define HDMI_CORE_SYS_HRES_L			0xE8 | ||||
| #define HDMI_CORE_SYS_HRES_H			0xEC | ||||
| #define HDMI_CORE_SYS_VRES_L			0xF0 | ||||
| #define HDMI_CORE_SYS_VRES_H			0xF4 | ||||
| #define HDMI_CORE_SYS_IADJUST			0xF8 | ||||
| #define HDMI_CORE_SYS_POLDETECT			0xFC | ||||
| #define HDMI_CORE_SYS_HWIDTH1			0x110 | ||||
| #define HDMI_CORE_SYS_HWIDTH2			0x114 | ||||
| #define HDMI_CORE_SYS_VWIDTH			0x11C | ||||
| #define HDMI_CORE_SYS_VID_CTRL			0x120 | ||||
| #define HDMI_CORE_SYS_VID_ACEN			0x124 | ||||
| #define HDMI_CORE_SYS_VID_MODE			0x128 | ||||
| #define HDMI_CORE_SYS_VID_BLANK1		0x12C | ||||
| #define HDMI_CORE_SYS_VID_BLANK2		0x130 | ||||
| #define HDMI_CORE_SYS_VID_BLANK3		0x134 | ||||
| #define HDMI_CORE_SYS_DC_HEADER			0x138 | ||||
| #define HDMI_CORE_SYS_VID_DITHER		0x13C | ||||
| #define HDMI_CORE_SYS_RGB2XVYCC_CT		0x140 | ||||
| #define HDMI_CORE_SYS_R2Y_COEFF_LOW		0x144 | ||||
| #define HDMI_CORE_SYS_R2Y_COEFF_UP		0x148 | ||||
| #define HDMI_CORE_SYS_G2Y_COEFF_LOW		0x14C | ||||
| #define HDMI_CORE_SYS_G2Y_COEFF_UP		0x150 | ||||
| #define HDMI_CORE_SYS_B2Y_COEFF_LOW		0x154 | ||||
| #define HDMI_CORE_SYS_B2Y_COEFF_UP		0x158 | ||||
| #define HDMI_CORE_SYS_R2CB_COEFF_LOW		0x15C | ||||
| #define HDMI_CORE_SYS_R2CB_COEFF_UP		0x160 | ||||
| #define HDMI_CORE_SYS_G2CB_COEFF_LOW		0x164 | ||||
| #define HDMI_CORE_SYS_G2CB_COEFF_UP		0x168 | ||||
| #define HDMI_CORE_SYS_B2CB_COEFF_LOW		0x16C | ||||
| #define HDMI_CORE_SYS_B2CB_COEFF_UP		0x170 | ||||
| #define HDMI_CORE_SYS_R2CR_COEFF_LOW		0x174 | ||||
| #define HDMI_CORE_SYS_R2CR_COEFF_UP		0x178 | ||||
| #define HDMI_CORE_SYS_G2CR_COEFF_LOW		0x17C | ||||
| #define HDMI_CORE_SYS_G2CR_COEFF_UP		0x180 | ||||
| #define HDMI_CORE_SYS_B2CR_COEFF_LOW		0x184 | ||||
| #define HDMI_CORE_SYS_B2CR_COEFF_UP		0x188 | ||||
| #define HDMI_CORE_SYS_RGB_OFFSET_LOW		0x18C | ||||
| #define HDMI_CORE_SYS_RGB_OFFSET_UP		0x190 | ||||
| #define HDMI_CORE_SYS_Y_OFFSET_LOW		0x194 | ||||
| #define HDMI_CORE_SYS_Y_OFFSET_UP		0x198 | ||||
| #define HDMI_CORE_SYS_CBCR_OFFSET_LOW		0x19C | ||||
| #define HDMI_CORE_SYS_CBCR_OFFSET_UP		0x1A0 | ||||
| #define HDMI_CORE_SYS_INTR_STATE		0x1C0 | ||||
| #define HDMI_CORE_SYS_INTR1			0x1C4 | ||||
| #define HDMI_CORE_SYS_INTR2			0x1C8 | ||||
| #define HDMI_CORE_SYS_INTR3			0x1CC | ||||
| #define HDMI_CORE_SYS_INTR4			0x1D0 | ||||
| #define HDMI_CORE_SYS_INTR_UNMASK1		0x1D4 | ||||
| #define HDMI_CORE_SYS_INTR_UNMASK2		0x1D8 | ||||
| #define HDMI_CORE_SYS_INTR_UNMASK3		0x1DC | ||||
| #define HDMI_CORE_SYS_INTR_UNMASK4		0x1E0 | ||||
| #define HDMI_CORE_SYS_INTR_CTRL			0x1E4 | ||||
| #define HDMI_CORE_SYS_TMDS_CTRL			0x208 | ||||
| 
 | ||||
| /* value definitions for HDMI_CORE_SYS_SYS_CTRL1 fields */ | ||||
| #define HDMI_CORE_SYS_SYS_CTRL1_VEN_FOLLOWVSYNC	0x1 | ||||
| #define HDMI_CORE_SYS_SYS_CTRL1_HEN_FOLLOWHSYNC	0x1 | ||||
| #define HDMI_CORE_SYS_SYS_CTRL1_BSEL_24BITBUS	0x1 | ||||
| #define HDMI_CORE_SYS_SYS_CTRL1_EDGE_RISINGEDGE	0x1 | ||||
| 
 | ||||
| /* HDMI DDC E-DID */ | ||||
| #define HDMI_CORE_DDC_ADDR			0x3B4 | ||||
| #define HDMI_CORE_DDC_SEGM			0x3B8 | ||||
| #define HDMI_CORE_DDC_OFFSET			0x3BC | ||||
| #define HDMI_CORE_DDC_COUNT1			0x3C0 | ||||
| #define HDMI_CORE_DDC_COUNT2			0x3C4 | ||||
| #define HDMI_CORE_DDC_STATUS			0x3C8 | ||||
| #define HDMI_CORE_DDC_CMD			0x3CC | ||||
| #define HDMI_CORE_DDC_DATA			0x3D0 | ||||
| 
 | ||||
| /* HDMI IP Core Audio Video */ | ||||
| 
 | ||||
| #define HDMI_CORE_AV_ACR_CTRL			0x4 | ||||
| #define HDMI_CORE_AV_FREQ_SVAL			0x8 | ||||
| #define HDMI_CORE_AV_N_SVAL1			0xC | ||||
| #define HDMI_CORE_AV_N_SVAL2			0x10 | ||||
| #define HDMI_CORE_AV_N_SVAL3			0x14 | ||||
| #define HDMI_CORE_AV_CTS_SVAL1			0x18 | ||||
| #define HDMI_CORE_AV_CTS_SVAL2			0x1C | ||||
| #define HDMI_CORE_AV_CTS_SVAL3			0x20 | ||||
| #define HDMI_CORE_AV_CTS_HVAL1			0x24 | ||||
| #define HDMI_CORE_AV_CTS_HVAL2			0x28 | ||||
| #define HDMI_CORE_AV_CTS_HVAL3			0x2C | ||||
| #define HDMI_CORE_AV_AUD_MODE			0x50 | ||||
| #define HDMI_CORE_AV_SPDIF_CTRL			0x54 | ||||
| #define HDMI_CORE_AV_HW_SPDIF_FS		0x60 | ||||
| #define HDMI_CORE_AV_SWAP_I2S			0x64 | ||||
| #define HDMI_CORE_AV_SPDIF_ERTH			0x6C | ||||
| #define HDMI_CORE_AV_I2S_IN_MAP			0x70 | ||||
| #define HDMI_CORE_AV_I2S_IN_CTRL		0x74 | ||||
| #define HDMI_CORE_AV_I2S_CHST0			0x78 | ||||
| #define HDMI_CORE_AV_I2S_CHST1			0x7C | ||||
| #define HDMI_CORE_AV_I2S_CHST2			0x80 | ||||
| #define HDMI_CORE_AV_I2S_CHST4			0x84 | ||||
| #define HDMI_CORE_AV_I2S_CHST5			0x88 | ||||
| #define HDMI_CORE_AV_ASRC			0x8C | ||||
| #define HDMI_CORE_AV_I2S_IN_LEN			0x90 | ||||
| #define HDMI_CORE_AV_HDMI_CTRL			0xBC | ||||
| #define HDMI_CORE_AV_AUDO_TXSTAT		0xC0 | ||||
| #define HDMI_CORE_AV_AUD_PAR_BUSCLK_1		0xCC | ||||
| #define HDMI_CORE_AV_AUD_PAR_BUSCLK_2		0xD0 | ||||
| #define HDMI_CORE_AV_AUD_PAR_BUSCLK_3		0xD4 | ||||
| #define HDMI_CORE_AV_TEST_TXCTRL		0xF0 | ||||
| #define HDMI_CORE_AV_DPD			0xF4 | ||||
| #define HDMI_CORE_AV_PB_CTRL1			0xF8 | ||||
| #define HDMI_CORE_AV_PB_CTRL2			0xFC | ||||
| #define HDMI_CORE_AV_AVI_BASE			0x100 | ||||
| #define HDMI_CORE_AV_AVI_TYPE			0x100 | ||||
| #define HDMI_CORE_AV_AVI_VERS			0x104 | ||||
| #define HDMI_CORE_AV_AVI_LEN			0x108 | ||||
| #define HDMI_CORE_AV_AVI_CHSUM			0x10C | ||||
| #define HDMI_CORE_AV_AVI_DBYTE(n)		(n * 4 + 0x110) | ||||
| #define HDMI_CORE_AV_SPD_TYPE			0x180 | ||||
| #define HDMI_CORE_AV_SPD_VERS			0x184 | ||||
| #define HDMI_CORE_AV_SPD_LEN			0x188 | ||||
| #define HDMI_CORE_AV_SPD_CHSUM			0x18C | ||||
| #define HDMI_CORE_AV_SPD_DBYTE(n)		(n * 4 + 0x190) | ||||
| #define HDMI_CORE_AV_AUDIO_TYPE			0x200 | ||||
| #define HDMI_CORE_AV_AUDIO_VERS			0x204 | ||||
| #define HDMI_CORE_AV_AUDIO_LEN			0x208 | ||||
| #define HDMI_CORE_AV_AUDIO_CHSUM		0x20C | ||||
| #define HDMI_CORE_AV_AUD_DBYTE(n)		(n * 4 + 0x210) | ||||
| #define HDMI_CORE_AV_MPEG_TYPE			0x280 | ||||
| #define HDMI_CORE_AV_MPEG_VERS			0x284 | ||||
| #define HDMI_CORE_AV_MPEG_LEN			0x288 | ||||
| #define HDMI_CORE_AV_MPEG_CHSUM			0x28C | ||||
| #define HDMI_CORE_AV_MPEG_DBYTE(n)		(n * 4 + 0x290) | ||||
| #define HDMI_CORE_AV_GEN_DBYTE(n)		(n * 4 + 0x300) | ||||
| #define HDMI_CORE_AV_CP_BYTE1			0x37C | ||||
| #define HDMI_CORE_AV_GEN2_DBYTE(n)		(n * 4 + 0x380) | ||||
| #define HDMI_CORE_AV_CEC_ADDR_ID		0x3FC | ||||
| 
 | ||||
| #define HDMI_CORE_AV_SPD_DBYTE_ELSIZE		0x4 | ||||
| #define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE		0x4 | ||||
| #define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE		0x4 | ||||
| #define HDMI_CORE_AV_GEN_DBYTE_ELSIZE		0x4 | ||||
| 
 | ||||
| #define HDMI_CORE_AV_AVI_DBYTE_NELEMS		15 | ||||
| #define HDMI_CORE_AV_SPD_DBYTE_NELEMS		27 | ||||
| #define HDMI_CORE_AV_AUD_DBYTE_NELEMS		10 | ||||
| #define HDMI_CORE_AV_MPEG_DBYTE_NELEMS		27 | ||||
| #define HDMI_CORE_AV_GEN_DBYTE_NELEMS		31 | ||||
| #define HDMI_CORE_AV_GEN2_DBYTE_NELEMS		31 | ||||
| 
 | ||||
| enum hdmi_core_inputbus_width { | ||||
| 	HDMI_INPUT_8BIT = 0, | ||||
| 	HDMI_INPUT_10BIT = 1, | ||||
| 	HDMI_INPUT_12BIT = 2 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_dither_trunc { | ||||
| 	HDMI_OUTPUTTRUNCATION_8BIT = 0, | ||||
| 	HDMI_OUTPUTTRUNCATION_10BIT = 1, | ||||
| 	HDMI_OUTPUTTRUNCATION_12BIT = 2, | ||||
| 	HDMI_OUTPUTDITHER_8BIT = 3, | ||||
| 	HDMI_OUTPUTDITHER_10BIT = 4, | ||||
| 	HDMI_OUTPUTDITHER_12BIT = 5 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_deepcolor_ed { | ||||
| 	HDMI_DEEPCOLORPACKECTDISABLE = 0, | ||||
| 	HDMI_DEEPCOLORPACKECTENABLE = 1 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_packet_mode { | ||||
| 	HDMI_PACKETMODERESERVEDVALUE = 0, | ||||
| 	HDMI_PACKETMODE24BITPERPIXEL = 4, | ||||
| 	HDMI_PACKETMODE30BITPERPIXEL = 5, | ||||
| 	HDMI_PACKETMODE36BITPERPIXEL = 6, | ||||
| 	HDMI_PACKETMODE48BITPERPIXEL = 7 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_tclkselclkmult { | ||||
| 	HDMI_FPLL05IDCK = 0, | ||||
| 	HDMI_FPLL10IDCK = 1, | ||||
| 	HDMI_FPLL20IDCK = 2, | ||||
| 	HDMI_FPLL40IDCK = 3 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_core_packet_ctrl { | ||||
| 	HDMI_PACKETENABLE = 1, | ||||
| 	HDMI_PACKETDISABLE = 0, | ||||
| 	HDMI_PACKETREPEATON = 1, | ||||
| 	HDMI_PACKETREPEATOFF = 0 | ||||
| }; | ||||
| 
 | ||||
| enum hdmi_audio_i2s_config { | ||||
| 	HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, | ||||
| 	HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, | ||||
| 	HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, | ||||
| 	HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, | ||||
| 	HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, | ||||
| 	HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, | ||||
| 	HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, | ||||
| 	HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, | ||||
| 	HDMI_AUDIO_I2S_SD0_EN = 1, | ||||
| 	HDMI_AUDIO_I2S_SD1_EN = 1 << 1, | ||||
| 	HDMI_AUDIO_I2S_SD2_EN = 1 << 2, | ||||
| 	HDMI_AUDIO_I2S_SD3_EN = 1 << 3, | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_video_config { | ||||
| 	enum hdmi_core_inputbus_width	ip_bus_width; | ||||
| 	enum hdmi_core_dither_trunc	op_dither_truc; | ||||
| 	enum hdmi_core_deepcolor_ed	deep_color_pkt; | ||||
| 	enum hdmi_core_packet_mode	pkt_mode; | ||||
| 	enum hdmi_core_hdmi_dvi		hdmi_dvi; | ||||
| 	enum hdmi_core_tclkselclkmult	tclk_sel_clkmult; | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_packet_enable_repeat { | ||||
| 	u32	audio_pkt; | ||||
| 	u32	audio_pkt_repeat; | ||||
| 	u32	avi_infoframe; | ||||
| 	u32	avi_infoframe_repeat; | ||||
| 	u32	gen_cntrl_pkt; | ||||
| 	u32	gen_cntrl_pkt_repeat; | ||||
| 	u32	generic_pkt; | ||||
| 	u32	generic_pkt_repeat; | ||||
| }; | ||||
| 
 | ||||
| int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len); | ||||
| void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_config *cfg); | ||||
| void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s); | ||||
| int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core); | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||||
| int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp); | ||||
| void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp); | ||||
| int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 		struct omap_dss_audio *audio, u32 pclk); | ||||
| int hdmi4_audio_get_dma_port(u32 *offset, u32 *size); | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										821
									
								
								drivers/video/fbdev/omap2/dss/hdmi5.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										821
									
								
								drivers/video/fbdev/omap2/dss/hdmi5.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,821 @@ | |||
| /*
 | ||||
|  * HDMI driver for OMAP5 | ||||
|  * | ||||
|  * Copyright (C) 2014 Texas Instruments Incorporated | ||||
|  * | ||||
|  * Authors: | ||||
|  *	Yong Zhi | ||||
|  *	Mythri pk | ||||
|  *	Archit Taneja <archit@ti.com> | ||||
|  *	Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMI" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "hdmi5_core.h" | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static struct { | ||||
| 	struct mutex lock; | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	struct hdmi_wp_data	wp; | ||||
| 	struct hdmi_pll_data	pll; | ||||
| 	struct hdmi_phy_data	phy; | ||||
| 	struct hdmi_core_data	core; | ||||
| 
 | ||||
| 	struct hdmi_config cfg; | ||||
| 
 | ||||
| 	struct clk *sys_clk; | ||||
| 	struct regulator *vdda_reg; | ||||
| 
 | ||||
| 	bool core_enabled; | ||||
| 
 | ||||
| 	struct omap_dss_device output; | ||||
| } hdmi; | ||||
| 
 | ||||
| static int hdmi_runtime_get(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_runtime_get\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_get_sync(&hdmi.pdev->dev); | ||||
| 	WARN_ON(r < 0); | ||||
| 	if (r < 0) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_runtime_put(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_runtime_put\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_put_sync(&hdmi.pdev->dev); | ||||
| 	WARN_ON(r < 0 && r != -ENOSYS); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t hdmi_irq_handler(int irq, void *data) | ||||
| { | ||||
| 	struct hdmi_wp_data *wp = data; | ||||
| 	u32 irqstatus; | ||||
| 
 | ||||
| 	irqstatus = hdmi_wp_get_irqstatus(wp); | ||||
| 	hdmi_wp_set_irqstatus(wp, irqstatus); | ||||
| 
 | ||||
| 	if ((irqstatus & HDMI_IRQ_LINK_CONNECT) && | ||||
| 			irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | ||||
| 		u32 v; | ||||
| 		/*
 | ||||
| 		 * If we get both connect and disconnect interrupts at the same | ||||
| 		 * time, turn off the PHY, clear interrupts, and restart, which | ||||
| 		 * raises connect interrupt if a cable is connected, or nothing | ||||
| 		 * if cable is not connected. | ||||
| 		 */ | ||||
| 
 | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * We always get bogus CONNECT & DISCONNECT interrupts when | ||||
| 		 * setting the PHY to LDOON. To ignore those, we force the RXDET | ||||
| 		 * line to 0 until the PHY power state has been changed. | ||||
| 		 */ | ||||
| 		v = hdmi_read_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL); | ||||
| 		v = FLD_MOD(v, 1, 15, 15); /* FORCE_RXDET_HIGH */ | ||||
| 		v = FLD_MOD(v, 0, 14, 7); /* RXDET_LINE */ | ||||
| 		hdmi_write_reg(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, v); | ||||
| 
 | ||||
| 		hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT | | ||||
| 				HDMI_IRQ_LINK_DISCONNECT); | ||||
| 
 | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 
 | ||||
| 		REG_FLD_MOD(hdmi.phy.base, HDMI_TXPHY_PAD_CFG_CTRL, 0, 15, 15); | ||||
| 
 | ||||
| 	} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) { | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON); | ||||
| 	} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) { | ||||
| 		hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 	} | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_init_regulator(void) | ||||
| { | ||||
| 	int r; | ||||
| 	struct regulator *reg; | ||||
| 
 | ||||
| 	if (hdmi.vdda_reg != NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); | ||||
| 	if (IS_ERR(reg)) { | ||||
| 		DSSERR("can't get VDDA regulator\n"); | ||||
| 		return PTR_ERR(reg); | ||||
| 	} | ||||
| 
 | ||||
| 	if (regulator_can_change_voltage(reg)) { | ||||
| 		r = regulator_set_voltage(reg, 1800000, 1800000); | ||||
| 		if (r) { | ||||
| 			devm_regulator_put(reg); | ||||
| 			DSSWARN("can't set the regulator voltage\n"); | ||||
| 			return r; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi.vdda_reg = reg; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_power_on_core(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = regulator_enable(hdmi.vdda_reg); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_runtime_get; | ||||
| 
 | ||||
| 	/* Make selection of HDMI in DSS */ | ||||
| 	dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | ||||
| 
 | ||||
| 	hdmi.core_enabled = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_runtime_get: | ||||
| 	regulator_disable(hdmi.vdda_reg); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_power_off_core(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi.core_enabled = false; | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	regulator_disable(hdmi.vdda_reg); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_power_on_full(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 	struct omap_video_timings *p; | ||||
| 	struct omap_overlay_manager *mgr = hdmi.output.manager; | ||||
| 	unsigned long phy; | ||||
| 
 | ||||
| 	r = hdmi_power_on_core(dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	p = &hdmi.cfg.timings; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); | ||||
| 
 | ||||
| 	/* the functions below use kHz pixel clock. TODO: change to Hz */ | ||||
| 	phy = p->pixelclock / 1000; | ||||
| 
 | ||||
| 	hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); | ||||
| 
 | ||||
| 	/* disable and clear irqs */ | ||||
| 	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | ||||
| 	hdmi_wp_set_irqstatus(&hdmi.wp, | ||||
| 			hdmi_wp_get_irqstatus(&hdmi.wp)); | ||||
| 
 | ||||
| 	/* config the PLL and PHY hdmi_set_pll_pwrfirst */ | ||||
| 	r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); | ||||
| 	if (r) { | ||||
| 		DSSDBG("Failed to lock PLL\n"); | ||||
| 		goto err_pll_enable; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); | ||||
| 	if (r) { | ||||
| 		DSSDBG("Failed to start PHY\n"); | ||||
| 		goto err_phy_cfg; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_LDOON); | ||||
| 	if (r) | ||||
| 		goto err_phy_pwr; | ||||
| 
 | ||||
| 	hdmi5_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg); | ||||
| 
 | ||||
| 	/* bypass TV gamma table */ | ||||
| 	dispc_enable_gamma_table(0); | ||||
| 
 | ||||
| 	/* tv size */ | ||||
| 	dss_mgr_set_timings(mgr, p); | ||||
| 
 | ||||
| 	r = hdmi_wp_video_start(&hdmi.wp); | ||||
| 	if (r) | ||||
| 		goto err_vid_enable; | ||||
| 
 | ||||
| 	r = dss_mgr_enable(mgr); | ||||
| 	if (r) | ||||
| 		goto err_mgr_enable; | ||||
| 
 | ||||
| 	hdmi_wp_set_irqenable(&hdmi.wp, | ||||
| 			HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_mgr_enable: | ||||
| 	hdmi_wp_video_stop(&hdmi.wp); | ||||
| err_vid_enable: | ||||
| 	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | ||||
| err_phy_pwr: | ||||
| err_phy_cfg: | ||||
| 	hdmi_pll_disable(&hdmi.pll, &hdmi.wp); | ||||
| err_pll_enable: | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| 	return -EIO; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_power_off_full(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = hdmi.output.manager; | ||||
| 
 | ||||
| 	hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); | ||||
| 
 | ||||
| 	dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 	hdmi_wp_video_stop(&hdmi.wp); | ||||
| 
 | ||||
| 	hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); | ||||
| 
 | ||||
| 	hdmi_pll_disable(&hdmi.pll, &hdmi.wp); | ||||
| 
 | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_display_check_timing(struct omap_dss_device *dssdev, | ||||
| 					struct omap_video_timings *timings) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_set_timing(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi.cfg.timings = *timings; | ||||
| 
 | ||||
| 	dispc_set_tv_pclk(timings->pixelclock); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	*timings = hdmi.cfg.timings; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_dump_regs(struct seq_file *s) | ||||
| { | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (hdmi_runtime_get()) { | ||||
| 		mutex_unlock(&hdmi.lock); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi_wp_dump(&hdmi.wp, s); | ||||
| 	hdmi_pll_dump(&hdmi.pll, s); | ||||
| 	hdmi_phy_dump(&hdmi.phy, s); | ||||
| 	hdmi5_core_dump(&hdmi.core, s); | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int read_edid(u8 *buf, int len) | ||||
| { | ||||
| 	int r; | ||||
| 	int idlemode; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_runtime_get(); | ||||
| 	BUG_ON(r); | ||||
| 
 | ||||
| 	idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); | ||||
| 	/* No-idle mode */ | ||||
| 	REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); | ||||
| 
 | ||||
| 	r = hdmi5_read_edid(&hdmi.core,  buf, len); | ||||
| 
 | ||||
| 	REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); | ||||
| 
 | ||||
| 	hdmi_runtime_put(); | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_display_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	DSSDBG("ENTER hdmi_display_enable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (out == NULL || out->manager == NULL) { | ||||
| 		DSSERR("failed to enable display: no output/manager\n"); | ||||
| 		r = -ENODEV; | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_power_on_full(dssdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to power on device\n"); | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err0: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_display_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_display_disable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi_power_off_full(dssdev); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_core_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r = 0; | ||||
| 
 | ||||
| 	DSSDBG("ENTER omapdss_hdmi_core_enable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_power_on_core(dssdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to power on device\n"); | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err0: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	DSSDBG("Enter omapdss_hdmi_core_disable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	hdmi_power_off_core(dssdev); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_get_clocks(struct platform_device *pdev) | ||||
| { | ||||
| 	struct clk *clk; | ||||
| 
 | ||||
| 	clk = devm_clk_get(&pdev->dev, "sys_clk"); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		DSSERR("can't get sys_clk\n"); | ||||
| 		return PTR_ERR(clk); | ||||
| 	} | ||||
| 
 | ||||
| 	hdmi.sys_clk = clk; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_connect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = hdmi_init_regulator(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||||
| 	if (!mgr) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = dss_mgr_connect(mgr, dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = omapdss_output_set_device(dssdev, dst); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to connect output to new device: %s\n", | ||||
| 				dst->name); | ||||
| 		dss_mgr_disconnect(mgr, dssdev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_disconnect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	WARN_ON(dst != dssdev->dst); | ||||
| 
 | ||||
| 	if (dst != dssdev->dst) | ||||
| 		return; | ||||
| 
 | ||||
| 	omapdss_output_unset_device(dssdev); | ||||
| 
 | ||||
| 	if (dssdev->manager) | ||||
| 		dss_mgr_disconnect(dssdev->manager, dssdev); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_read_edid(struct omap_dss_device *dssdev, | ||||
| 		u8 *edid, int len) | ||||
| { | ||||
| 	bool need_enable; | ||||
| 	int r; | ||||
| 
 | ||||
| 	need_enable = hdmi.core_enabled == false; | ||||
| 
 | ||||
| 	if (need_enable) { | ||||
| 		r = hdmi_core_enable(dssdev); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	r = read_edid(edid, len); | ||||
| 
 | ||||
| 	if (need_enable) | ||||
| 		hdmi_core_disable(dssdev); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| static int hdmi_audio_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { | ||||
| 		r = -EPERM; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_audio_enable(&hdmi.wp, true); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi_wp_audio_enable(&hdmi.wp, false); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_start(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return hdmi_wp_audio_core_req_enable(&hdmi.wp, true); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_stop(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	hdmi_wp_audio_core_req_enable(&hdmi.wp, false); | ||||
| } | ||||
| 
 | ||||
| static bool hdmi_audio_supported(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	bool r; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_config(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_audio *audio) | ||||
| { | ||||
| 	int r; | ||||
| 	u32 pclk = hdmi.cfg.timings.pixelclock; | ||||
| 
 | ||||
| 	mutex_lock(&hdmi.lock); | ||||
| 
 | ||||
| 	if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { | ||||
| 		r = -EPERM; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	mutex_unlock(&hdmi.lock); | ||||
| 	return r; | ||||
| } | ||||
| #else | ||||
| static int hdmi_audio_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_start(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_audio_stop(struct omap_dss_device *dssdev) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static bool hdmi_audio_supported(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_audio_config(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_audio *audio) | ||||
| { | ||||
| 	return -EPERM; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int hdmi_set_infoframe(struct omap_dss_device *dssdev, | ||||
| 		const struct hdmi_avi_infoframe *avi) | ||||
| { | ||||
| 	hdmi.cfg.infoframe = *avi; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, | ||||
| 		bool hdmi_mode) | ||||
| { | ||||
| 	hdmi.cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct omapdss_hdmi_ops hdmi_ops = { | ||||
| 	.connect		= hdmi_connect, | ||||
| 	.disconnect		= hdmi_disconnect, | ||||
| 
 | ||||
| 	.enable			= hdmi_display_enable, | ||||
| 	.disable		= hdmi_display_disable, | ||||
| 
 | ||||
| 	.check_timings		= hdmi_display_check_timing, | ||||
| 	.set_timings		= hdmi_display_set_timing, | ||||
| 	.get_timings		= hdmi_display_get_timings, | ||||
| 
 | ||||
| 	.read_edid		= hdmi_read_edid, | ||||
| 	.set_infoframe		= hdmi_set_infoframe, | ||||
| 	.set_hdmi_mode		= hdmi_set_hdmi_mode, | ||||
| 
 | ||||
| 	.audio_enable		= hdmi_audio_enable, | ||||
| 	.audio_disable		= hdmi_audio_disable, | ||||
| 	.audio_start		= hdmi_audio_start, | ||||
| 	.audio_stop		= hdmi_audio_stop, | ||||
| 	.audio_supported	= hdmi_audio_supported, | ||||
| 	.audio_config		= hdmi_audio_config, | ||||
| }; | ||||
| 
 | ||||
| static void hdmi_init_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	out->dev = &pdev->dev; | ||||
| 	out->id = OMAP_DSS_OUTPUT_HDMI; | ||||
| 	out->output_type = OMAP_DISPLAY_TYPE_HDMI; | ||||
| 	out->name = "hdmi.0"; | ||||
| 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | ||||
| 	out->ops.hdmi = &hdmi_ops; | ||||
| 	out->owner = THIS_MODULE; | ||||
| 
 | ||||
| 	omapdss_register_output(out); | ||||
| } | ||||
| 
 | ||||
| static void __exit hdmi_uninit_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &hdmi.output; | ||||
| 
 | ||||
| 	omapdss_unregister_output(out); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_probe_of(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *node = pdev->dev.of_node; | ||||
| 	struct device_node *ep; | ||||
| 	int r; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_first_endpoint(node); | ||||
| 	if (!ep) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	of_node_put(ep); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| /* HDMI HW IP initialisation */ | ||||
| static int omapdss_hdmihw_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int r; | ||||
| 	int irq; | ||||
| 
 | ||||
| 	hdmi.pdev = pdev; | ||||
| 
 | ||||
| 	mutex_init(&hdmi.lock); | ||||
| 
 | ||||
| 	if (pdev->dev.of_node) { | ||||
| 		r = hdmi_probe_of(pdev); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	r = hdmi_wp_init(pdev, &hdmi.wp); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_pll_init(pdev, &hdmi.pll); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_phy_init(pdev, &hdmi.phy); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi5_core_init(pdev, &hdmi.core); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_get_clocks(pdev); | ||||
| 	if (r) { | ||||
| 		DSSERR("can't get clocks\n"); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	irq = platform_get_irq(pdev, 0); | ||||
| 	if (irq < 0) { | ||||
| 		DSSERR("platform_get_irq failed\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	r = devm_request_threaded_irq(&pdev->dev, irq, | ||||
| 			NULL, hdmi_irq_handler, | ||||
| 			IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); | ||||
| 	if (r) { | ||||
| 		DSSERR("HDMI IRQ request failed\n"); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	pm_runtime_enable(&pdev->dev); | ||||
| 
 | ||||
| 	hdmi_init_output(pdev); | ||||
| 
 | ||||
| 	dss_debugfs_create_file("hdmi", hdmi_dump_regs); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	hdmi_uninit_output(pdev); | ||||
| 
 | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_runtime_suspend(struct device *dev) | ||||
| { | ||||
| 	clk_disable_unprepare(hdmi.sys_clk); | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_runtime_resume(struct device *dev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r < 0) | ||||
| 		return r; | ||||
| 
 | ||||
| 	clk_prepare_enable(hdmi.sys_clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops hdmi_pm_ops = { | ||||
| 	.runtime_suspend = hdmi_runtime_suspend, | ||||
| 	.runtime_resume = hdmi_runtime_resume, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id hdmi_of_match[] = { | ||||
| 	{ .compatible = "ti,omap5-hdmi", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver omapdss_hdmihw_driver = { | ||||
| 	.probe		= omapdss_hdmihw_probe, | ||||
| 	.remove         = __exit_p(omapdss_hdmihw_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_hdmi5", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.pm	= &hdmi_pm_ops, | ||||
| 		.of_match_table = hdmi_of_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init hdmi5_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&omapdss_hdmihw_driver); | ||||
| } | ||||
| 
 | ||||
| void __exit hdmi5_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omapdss_hdmihw_driver); | ||||
| } | ||||
							
								
								
									
										916
									
								
								drivers/video/fbdev/omap2/dss/hdmi5_core.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										916
									
								
								drivers/video/fbdev/omap2/dss/hdmi5_core.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,916 @@ | |||
| /*
 | ||||
|  * OMAP5 HDMI CORE IP driver library | ||||
|  * | ||||
|  * Copyright (C) 2014 Texas Instruments Incorporated | ||||
|  * | ||||
|  * Authors: | ||||
|  *	Yong Zhi | ||||
|  *	Mythri pk | ||||
|  *	Archit Taneja <archit@ti.com> | ||||
|  *	Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/seq_file.h> | ||||
| #include <drm/drm_edid.h> | ||||
| #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| #include <sound/asound.h> | ||||
| #include <sound/asoundef.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "hdmi5_core.h" | ||||
| 
 | ||||
| /* only 24 bit color depth used for now */ | ||||
| static const struct csc_table csc_table_deepcolor[] = { | ||||
| 	/* HDMI_DEEP_COLOR_24BIT */ | ||||
| 	[0] = { 7036, 0, 0, 32, 0, 7036, 0, 32, 0, 0, 7036, 32, }, | ||||
| 	/* HDMI_DEEP_COLOR_30BIT */ | ||||
| 	[1] = { 7015, 0, 0, 128, 0, 7015, 0, 128, 0, 0, 7015, 128, }, | ||||
| 	/* HDMI_DEEP_COLOR_36BIT */ | ||||
| 	[2] = { 7010, 0, 0, 512, 0, 7010, 0, 512, 0, 0, 7010, 512, }, | ||||
| 	/* FULL RANGE */ | ||||
| 	[3] = { 8192, 0, 0, 0, 0, 8192, 0, 0, 0, 0, 8192, 0, }, | ||||
| }; | ||||
| 
 | ||||
| static void hdmi_core_ddc_init(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	const unsigned long long iclk = 266000000;	/* DSS L3 ICLK */ | ||||
| 	const unsigned ss_scl_high = 4000;		/* ns */ | ||||
| 	const unsigned ss_scl_low = 4700;		/* ns */ | ||||
| 	const unsigned fs_scl_high = 600;		/* ns */ | ||||
| 	const unsigned fs_scl_low = 1300;		/* ns */ | ||||
| 	const unsigned sda_hold = 300;			/* ns */ | ||||
| 	const unsigned sfr_div = 10; | ||||
| 	unsigned long long sfr; | ||||
| 	unsigned v; | ||||
| 
 | ||||
| 	sfr = iclk / sfr_div;	/* SFR_DIV */ | ||||
| 	sfr /= 1000;		/* SFR clock in kHz */ | ||||
| 
 | ||||
| 	/* Reset */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SOFTRSTZ, 0, 0, 0); | ||||
| 	if (hdmi_wait_for_bit_change(base, HDMI_CORE_I2CM_SOFTRSTZ, | ||||
| 				0, 0, 1) != 1) | ||||
| 		DSSERR("HDMI I2CM reset failed\n"); | ||||
| 
 | ||||
| 	/* Standard (0) or Fast (1) Mode */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_DIV, 0, 3, 3); | ||||
| 
 | ||||
| 	/* Standard Mode SCL High counter */ | ||||
| 	v = DIV_ROUND_UP_ULL(ss_scl_high * sfr, 1000000); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR, | ||||
| 			(v >> 8) & 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR, | ||||
| 			v & 0xff, 7, 0); | ||||
| 
 | ||||
| 	/* Standard Mode SCL Low counter */ | ||||
| 	v = DIV_ROUND_UP_ULL(ss_scl_low * sfr, 1000000); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR, | ||||
| 			(v >> 8) & 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR, | ||||
| 			v & 0xff, 7, 0); | ||||
| 
 | ||||
| 	/* Fast Mode SCL High Counter */ | ||||
| 	v = DIV_ROUND_UP_ULL(fs_scl_high * sfr, 1000000); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR, | ||||
| 			(v >> 8) & 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR, | ||||
| 			v & 0xff, 7, 0); | ||||
| 
 | ||||
| 	/* Fast Mode SCL Low Counter */ | ||||
| 	v = DIV_ROUND_UP_ULL(fs_scl_low * sfr, 1000000); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR, | ||||
| 			(v >> 8) & 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR, | ||||
| 			v & 0xff, 7, 0); | ||||
| 
 | ||||
| 	/* SDA Hold Time */ | ||||
| 	v = DIV_ROUND_UP_ULL(sda_hold * sfr, 1000000); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SDA_HOLD_ADDR, v & 0xff, 7, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SLAVE, 0x50, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGADDR, 0x30, 6, 0); | ||||
| 
 | ||||
| 	/* NACK_POL to high */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 7, 7); | ||||
| 
 | ||||
| 	/* NACK_MASK to unmasked */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 6, 6); | ||||
| 
 | ||||
| 	/* ARBITRATION_POL to high */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 3, 3); | ||||
| 
 | ||||
| 	/* ARBITRATION_MASK to unmasked */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x0, 2, 2); | ||||
| 
 | ||||
| 	/* DONE_POL to high */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 3, 3); | ||||
| 
 | ||||
| 	/* DONE_MASK to unmasked */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_ddc_uninit(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	/* Mask I2C interrupts */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2); | ||||
| } | ||||
| 
 | ||||
| static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	u8 cur_addr; | ||||
| 	char checksum = 0; | ||||
| 	const int retries = 1000; | ||||
| 	u8 seg_ptr = ext / 2; | ||||
| 	u8 edidbase = ((ext % 2) * 0x80); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * TODO: We use polling here, although we probably should use proper | ||||
| 	 * interrupts. | ||||
| 	 */ | ||||
| 	for (cur_addr = 0; cur_addr < 128; ++cur_addr) { | ||||
| 		int i; | ||||
| 
 | ||||
| 		/* clear ERROR and DONE */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0); | ||||
| 
 | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_I2CM_ADDRESS, | ||||
| 				edidbase + cur_addr, 7, 0); | ||||
| 
 | ||||
| 		if (seg_ptr) | ||||
| 			REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 1, 1); | ||||
| 		else | ||||
| 			REG_FLD_MOD(base, HDMI_CORE_I2CM_OPERATION, 1, 0, 0); | ||||
| 
 | ||||
| 		for (i = 0; i < retries; ++i) { | ||||
| 			u32 stat; | ||||
| 
 | ||||
| 			stat = REG_GET(base, HDMI_CORE_IH_I2CM_STAT0, 1, 0); | ||||
| 
 | ||||
| 			/* I2CM_ERROR */ | ||||
| 			if (stat & 1) { | ||||
| 				DSSERR("HDMI I2C Master Error\n"); | ||||
| 				return -EIO; | ||||
| 			} | ||||
| 
 | ||||
| 			/* I2CM_DONE */ | ||||
| 			if (stat & (1 << 1)) | ||||
| 				break; | ||||
| 
 | ||||
| 			usleep_range(250, 1000); | ||||
| 		} | ||||
| 
 | ||||
| 		if (i == retries) { | ||||
| 			DSSERR("HDMI I2C timeout reading EDID\n"); | ||||
| 			return -EIO; | ||||
| 		} | ||||
| 
 | ||||
| 		pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0); | ||||
| 		checksum += pedid[cur_addr]; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len) | ||||
| { | ||||
| 	int r, n, i; | ||||
| 	int max_ext_blocks = (len / 128) - 1; | ||||
| 
 | ||||
| 	if (len < 128) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	hdmi_core_ddc_init(core); | ||||
| 
 | ||||
| 	r = hdmi_core_ddc_edid(core, edid, 0); | ||||
| 	if (r) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	n = edid[0x7e]; | ||||
| 
 | ||||
| 	if (n > max_ext_blocks) | ||||
| 		n = max_ext_blocks; | ||||
| 
 | ||||
| 	for (i = 1; i <= n; i++) { | ||||
| 		r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i); | ||||
| 		if (r) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	hdmi_core_ddc_uninit(core); | ||||
| 
 | ||||
| 	return r ? r : len; | ||||
| } | ||||
| 
 | ||||
| void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s) | ||||
| { | ||||
| 
 | ||||
| #define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ | ||||
| 		hdmi_read_reg(core->base, r)) | ||||
| 
 | ||||
| 	DUMPCORE(HDMI_CORE_FC_INVIDCONF); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INHACTIV0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INHACTIV1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INHBLANK0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INHBLANK1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INVACTIV0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INVACTIV1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_INVBLANK); | ||||
| 	DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_HSYNCINDELAY1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_HSYNCINWIDTH1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_VSYNCINDELAY); | ||||
| 	DUMPCORE(HDMI_CORE_FC_VSYNCINWIDTH); | ||||
| 	DUMPCORE(HDMI_CORE_FC_CTRLDUR); | ||||
| 	DUMPCORE(HDMI_CORE_FC_EXCTRLDUR); | ||||
| 	DUMPCORE(HDMI_CORE_FC_EXCTRLSPAC); | ||||
| 	DUMPCORE(HDMI_CORE_FC_CH0PREAM); | ||||
| 	DUMPCORE(HDMI_CORE_FC_CH1PREAM); | ||||
| 	DUMPCORE(HDMI_CORE_FC_CH2PREAM); | ||||
| 	DUMPCORE(HDMI_CORE_FC_AVICONF0); | ||||
| 	DUMPCORE(HDMI_CORE_FC_AVICONF1); | ||||
| 	DUMPCORE(HDMI_CORE_FC_AVICONF2); | ||||
| 	DUMPCORE(HDMI_CORE_FC_AVIVID); | ||||
| 	DUMPCORE(HDMI_CORE_FC_PRCONF); | ||||
| 
 | ||||
| 	DUMPCORE(HDMI_CORE_MC_CLKDIS); | ||||
| 	DUMPCORE(HDMI_CORE_MC_SWRSTZREQ); | ||||
| 	DUMPCORE(HDMI_CORE_MC_FLOWCTRL); | ||||
| 	DUMPCORE(HDMI_CORE_MC_PHYRSTZ); | ||||
| 	DUMPCORE(HDMI_CORE_MC_LOCKONCLOCK); | ||||
| 
 | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SLAVE); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_ADDRESS); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_DATAO); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_DATAI); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_OPERATION); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_INT); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_CTLINT); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_DIV); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SEGADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SOFTRSTZ); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SEGPTR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR); | ||||
| 	DUMPCORE(HDMI_CORE_I2CM_SDA_HOLD_ADDR); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg, | ||||
| 			struct hdmi_config *cfg) | ||||
| { | ||||
| 	DSSDBG("hdmi_core_init\n"); | ||||
| 
 | ||||
| 	/* video core */ | ||||
| 	video_cfg->data_enable_pol = 1; /* It is always 1*/ | ||||
| 	video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level; | ||||
| 	video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res; | ||||
| 	video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1; | ||||
| 	video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp; | ||||
| 	video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp; | ||||
| 	video_cfg->hblank = cfg->timings.hfp + | ||||
| 				cfg->timings.hbp + cfg->timings.hsw - 1; | ||||
| 	video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level; | ||||
| 	video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res; | ||||
| 	video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw; | ||||
| 	video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp; | ||||
| 	video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp; | ||||
| 	video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */ | ||||
| 	video_cfg->vblank = cfg->timings.vsw + | ||||
| 				cfg->timings.vfp + cfg->timings.vbp; | ||||
| 	video_cfg->v_fc_config.hdmi_dvi_mode = cfg->hdmi_dvi_mode; | ||||
| 	video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace; | ||||
| } | ||||
| 
 | ||||
| /* DSS_HDMI_CORE_VIDEO_CONFIG */ | ||||
| static void hdmi_core_video_config(struct hdmi_core_data *core, | ||||
| 			struct hdmi_core_vid_config *cfg) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	unsigned char r = 0; | ||||
| 	bool vsync_pol, hsync_pol; | ||||
| 
 | ||||
| 	vsync_pol = | ||||
| 		cfg->v_fc_config.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH; | ||||
| 	hsync_pol = | ||||
| 		cfg->v_fc_config.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH; | ||||
| 
 | ||||
| 	/* Set hsync, vsync and data-enable polarity  */ | ||||
| 	r = hdmi_read_reg(base, HDMI_CORE_FC_INVIDCONF); | ||||
| 	r = FLD_MOD(r, vsync_pol, 6, 6); | ||||
| 	r = FLD_MOD(r, hsync_pol, 5, 5); | ||||
| 	r = FLD_MOD(r, cfg->data_enable_pol, 4, 4); | ||||
| 	r = FLD_MOD(r, cfg->vblank_osc, 1, 1); | ||||
| 	r = FLD_MOD(r, cfg->v_fc_config.timings.interlace, 0, 0); | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_INVIDCONF, r); | ||||
| 
 | ||||
| 	/* set x resolution */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV1, | ||||
| 			cfg->v_fc_config.timings.x_res >> 8, 4, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INHACTIV0, | ||||
| 			cfg->v_fc_config.timings.x_res & 0xFF, 7, 0); | ||||
| 
 | ||||
| 	/* set y resolution */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV1, | ||||
| 			cfg->v_fc_config.timings.y_res >> 8, 4, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INVACTIV0, | ||||
| 			cfg->v_fc_config.timings.y_res & 0xFF, 7, 0); | ||||
| 
 | ||||
| 	/* set horizontal blanking pixels */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK1, cfg->hblank >> 8, 4, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INHBLANK0, cfg->hblank & 0xFF, 7, 0); | ||||
| 
 | ||||
| 	/* set vertial blanking pixels */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INVBLANK, cfg->vblank, 7, 0); | ||||
| 
 | ||||
| 	/* set horizontal sync offset */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY1, | ||||
| 			cfg->v_fc_config.timings.hfp >> 8, 4, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINDELAY0, | ||||
| 			cfg->v_fc_config.timings.hfp & 0xFF, 7, 0); | ||||
| 
 | ||||
| 	/* set vertical sync offset */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINDELAY, | ||||
| 			cfg->v_fc_config.timings.vfp, 7, 0); | ||||
| 
 | ||||
| 	/* set horizontal sync pulse width */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH1, | ||||
| 			(cfg->v_fc_config.timings.hsw >> 8), 1, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_HSYNCINWIDTH0, | ||||
| 			cfg->v_fc_config.timings.hsw & 0xFF, 7, 0); | ||||
| 
 | ||||
| 	/*  set vertical sync pulse width */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_VSYNCINWIDTH, | ||||
| 			cfg->v_fc_config.timings.vsw, 5, 0); | ||||
| 
 | ||||
| 	/* select DVI mode */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF, | ||||
| 			cfg->v_fc_config.hdmi_dvi_mode, 3, 3); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	int clr_depth = 0;	/* 24 bit color depth */ | ||||
| 
 | ||||
| 	/* COLOR_DEPTH */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_PR_CD, clr_depth, 7, 4); | ||||
| 	/* BYPASS_EN */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 1, 6, 6); | ||||
| 	/* PP_EN */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 1 : 0, 5, 5); | ||||
| 	/* YCC422_EN */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_CONF, 0, 3, 3); | ||||
| 	/* PP_STUFFING */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, clr_depth ? 1 : 0, 1, 1); | ||||
| 	/* YCC422_STUFFING */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_STUFF, 1, 2, 2); | ||||
| 	/* OUTPUT_SELECTOR */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_CONF, clr_depth ? 0 : 2, 1, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_config_csc(struct hdmi_core_data *core) | ||||
| { | ||||
| 	int clr_depth = 0;	/* 24 bit color depth */ | ||||
| 
 | ||||
| 	/* CSC_COLORDEPTH */ | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_CSC_SCALE, clr_depth, 7, 4); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_config_video_sampler(struct hdmi_core_data *core) | ||||
| { | ||||
| 	int video_mapping = 1;	/* for 24 bit color depth */ | ||||
| 
 | ||||
| 	/* VIDEO_MAPPING */ | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_TX_INVID0, video_mapping, 4, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_write_avi_infoframe(struct hdmi_core_data *core, | ||||
| 	struct hdmi_avi_infoframe *frame) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	u8 data[HDMI_INFOFRAME_SIZE(AVI)]; | ||||
| 	u8 *ptr; | ||||
| 	unsigned y, a, b, s; | ||||
| 	unsigned c, m, r; | ||||
| 	unsigned itc, ec, q, sc; | ||||
| 	unsigned vic; | ||||
| 	unsigned yq, cn, pr; | ||||
| 
 | ||||
| 	hdmi_avi_infoframe_pack(frame, data, sizeof(data)); | ||||
| 
 | ||||
| 	print_hex_dump_debug("AVI: ", DUMP_PREFIX_NONE, 16, 1, data, | ||||
| 		HDMI_INFOFRAME_SIZE(AVI), false); | ||||
| 
 | ||||
| 	ptr = data + HDMI_INFOFRAME_HEADER_SIZE; | ||||
| 
 | ||||
| 	y = (ptr[0] >> 5) & 0x3; | ||||
| 	a = (ptr[0] >> 4) & 0x1; | ||||
| 	b = (ptr[0] >> 2) & 0x3; | ||||
| 	s = (ptr[0] >> 0) & 0x3; | ||||
| 
 | ||||
| 	c = (ptr[1] >> 6) & 0x3; | ||||
| 	m = (ptr[1] >> 4) & 0x3; | ||||
| 	r = (ptr[1] >> 0) & 0x3; | ||||
| 
 | ||||
| 	itc = (ptr[2] >> 7) & 0x1; | ||||
| 	ec = (ptr[2] >> 4) & 0x7; | ||||
| 	q = (ptr[2] >> 2) & 0x3; | ||||
| 	sc = (ptr[2] >> 0) & 0x3; | ||||
| 
 | ||||
| 	vic = ptr[3]; | ||||
| 
 | ||||
| 	yq = (ptr[4] >> 6) & 0x3; | ||||
| 	cn = (ptr[4] >> 4) & 0x3; | ||||
| 	pr = (ptr[4] >> 0) & 0xf; | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AVICONF0, | ||||
| 		(a << 6) | (s << 4) | (b << 2) | (y << 0)); | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AVICONF1, | ||||
| 		(c << 6) | (m << 4) | (r << 0)); | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AVICONF2, | ||||
| 		(itc << 7) | (ec << 4) | (q << 2) | (sc << 0)); | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AVIVID, vic); | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AVICONF3, | ||||
| 		(yq << 2) | (cn << 0)); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, pr, 3, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_csc_config(struct hdmi_core_data *core, | ||||
| 		struct csc_table csc_coeff) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_MSB, csc_coeff.a1 >> 8 , 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A1_LSB, csc_coeff.a1, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_MSB, csc_coeff.a2 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A2_LSB, csc_coeff.a2, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_MSB, csc_coeff.a3 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A3_LSB, csc_coeff.a3, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_MSB, csc_coeff.a4 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_A4_LSB, csc_coeff.a4, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_MSB, csc_coeff.b1 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B1_LSB, csc_coeff.b1, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_MSB, csc_coeff.b2 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B2_LSB, csc_coeff.b2, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_MSB, csc_coeff.b3 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B3_LSB, csc_coeff.b3, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_MSB, csc_coeff.b4 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_B4_LSB, csc_coeff.b4, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_MSB, csc_coeff.c1 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C1_LSB, csc_coeff.c1, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_MSB, csc_coeff.c2 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C2_LSB, csc_coeff.c2, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_MSB, csc_coeff.c3 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C3_LSB, csc_coeff.c3, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_MSB, csc_coeff.c4 >> 8, 6, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CSC_COEF_C4_LSB, csc_coeff.c4, 7, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_MC_FLOWCTRL, 0x1, 0, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_configure_range(struct hdmi_core_data *core) | ||||
| { | ||||
| 	struct csc_table csc_coeff = { 0 }; | ||||
| 
 | ||||
| 	/* support limited range with 24 bit color depth for now */ | ||||
| 	csc_coeff = csc_table_deepcolor[0]; | ||||
| 
 | ||||
| 	hdmi_core_csc_config(core, csc_coeff); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_enable_video_path(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	DSSDBG("hdmi_core_enable_video_path\n"); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_CTRLDUR, 0x0C, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLDUR, 0x20, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_EXCTRLSPAC, 0x01, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_CH0PREAM, 0x0B, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_CH1PREAM, 0x16, 5, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_CH2PREAM, 0x21, 5, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 0, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_MC_CLKDIS, 0x00, 1, 1); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_mask_interrupts(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	/* Master IRQ mask */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_MUTE, 0x3, 1, 0); | ||||
| 
 | ||||
| 	/* Mask all the interrupts in HDMI core */ | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_VP_MASK, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_MASK0, 0xe7, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_MASK1, 0xfb, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_MASK2, 0x3, 1, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 0x3, 3, 2); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 0x3, 1, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_CEC_MASK, 0x7f, 6, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 6, 6); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_CTLINT, 0x1, 2, 2); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_PHY_MASK0, 0xf3, 7, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0); | ||||
| 
 | ||||
| 	/* Clear all the current interrupt bits */ | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xe7, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xfb, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0x3, 1, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0x7, 2, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0x7f, 6, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0x3, 1, 0); | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0); | ||||
| } | ||||
| 
 | ||||
| static void hdmi_core_enable_interrupts(struct hdmi_core_data *core) | ||||
| { | ||||
| 	/* Unmute interrupts */ | ||||
| 	REG_FLD_MOD(core->base, HDMI_CORE_IH_MUTE, 0x0, 1, 0); | ||||
| } | ||||
| 
 | ||||
| int hdmi5_core_handle_irqs(struct hdmi_core_data *core) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT1, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_FC_STAT2, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_AS_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_PHY_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_I2CM_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_CEC_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_VP_STAT0, 0xff, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_IH_I2CMPHY_STAT0, 0xff, 7, 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_config *cfg) | ||||
| { | ||||
| 	struct omap_video_timings video_timing; | ||||
| 	struct hdmi_video_format video_format; | ||||
| 	struct hdmi_core_vid_config v_core_cfg; | ||||
| 
 | ||||
| 	hdmi_core_mask_interrupts(core); | ||||
| 
 | ||||
| 	hdmi_core_init(&v_core_cfg, cfg); | ||||
| 
 | ||||
| 	hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg); | ||||
| 
 | ||||
| 	hdmi_wp_video_config_timing(wp, &video_timing); | ||||
| 
 | ||||
| 	/* video config */ | ||||
| 	video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422; | ||||
| 
 | ||||
| 	hdmi_wp_video_config_format(wp, &video_format); | ||||
| 
 | ||||
| 	hdmi_wp_video_config_interface(wp, &video_timing); | ||||
| 
 | ||||
| 	/* support limited range with 24 bit color depth for now */ | ||||
| 	hdmi_core_configure_range(core); | ||||
| 	cfg->infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * configure core video part, set software reset in the core | ||||
| 	 */ | ||||
| 	v_core_cfg.packet_mode = HDMI_PACKETMODE24BITPERPIXEL; | ||||
| 
 | ||||
| 	hdmi_core_video_config(core, &v_core_cfg); | ||||
| 
 | ||||
| 	hdmi_core_config_video_packetizer(core); | ||||
| 	hdmi_core_config_csc(core); | ||||
| 	hdmi_core_config_video_sampler(core); | ||||
| 
 | ||||
| 	if (cfg->hdmi_dvi_mode == HDMI_HDMI) | ||||
| 		hdmi_core_write_avi_infoframe(core, &cfg->infoframe); | ||||
| 
 | ||||
| 	hdmi_core_enable_video_path(core); | ||||
| 
 | ||||
| 	hdmi_core_enable_interrupts(core); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| 
 | ||||
| static void hdmi5_core_audio_config(struct hdmi_core_data *core, | ||||
| 			struct hdmi_core_audio_config *cfg) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 	u8 val; | ||||
| 
 | ||||
| 	/* Mute audio before configuring */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0xf, 7, 4); | ||||
| 
 | ||||
| 	/* Set the N parameter */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_N1, cfg->n, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_N2, cfg->n >> 8, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_N3, cfg->n >> 16, 3, 0); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * CTS manual mode. Automatic mode is not supported when using audio | ||||
| 	 * parallel interface. | ||||
| 	 */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, 1, 4, 4); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_CTS1, cfg->cts, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_CTS2, cfg->cts >> 8, 7, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_CTS3, cfg->cts >> 16, 3, 0); | ||||
| 
 | ||||
| 	/* Layout of Audio Sample Packets: 2-channel or multichannels */ | ||||
| 	if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 0, 0); | ||||
| 	else | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 1, 0, 0); | ||||
| 
 | ||||
| 	/* Configure IEC-609580 Validity bits */ | ||||
| 	/* Channel 0 is valid */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 0, 0); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, 0, 4, 4); | ||||
| 
 | ||||
| 	if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) | ||||
| 		val = 1; | ||||
| 	else | ||||
| 		val = 0; | ||||
| 
 | ||||
| 	/* Channels 1, 2 setting */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 1, 1); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 5, 5); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 2, 2); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 6, 6); | ||||
| 	/* Channel 3 setting */ | ||||
| 	if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH) | ||||
| 		val = 1; | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 3, 3); | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSV, val, 7, 7); | ||||
| 
 | ||||
| 	/* Configure IEC-60958 User bits */ | ||||
| 	/* TODO: should be set by user. */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSU, 0, 7, 0); | ||||
| 
 | ||||
| 	/* Configure IEC-60958 Channel Status word */ | ||||
| 	/* CGMSA */ | ||||
| 	val = cfg->iec60958_cfg->status[5] & IEC958_AES5_CON_CGMSA; | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 5, 4); | ||||
| 
 | ||||
| 	/* Copyright */ | ||||
| 	val = (cfg->iec60958_cfg->status[0] & | ||||
| 			IEC958_AES0_CON_NOT_COPYRIGHT) >> 2; | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(0), val, 0, 0); | ||||
| 
 | ||||
| 	/* Category */ | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(1), | ||||
| 		cfg->iec60958_cfg->status[1]); | ||||
| 
 | ||||
| 	/* PCM audio mode */ | ||||
| 	val = (cfg->iec60958_cfg->status[0] & IEC958_AES0_CON_MODE) >> 6; | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 6, 4); | ||||
| 
 | ||||
| 	/* Source number */ | ||||
| 	val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE; | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 4); | ||||
| 
 | ||||
| 	/* Channel number right 0  */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0); | ||||
| 	/* Channel number right 1*/ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 4, 7, 4); | ||||
| 	/* Channel number right 2  */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 6, 3, 0); | ||||
| 	/* Channel number right 3*/ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(4), 8, 7, 4); | ||||
| 	/* Channel number left 0  */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 1, 3, 0); | ||||
| 	/* Channel number left 1*/ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(5), 3, 7, 4); | ||||
| 	/* Channel number left 2  */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 5, 3, 0); | ||||
| 	/* Channel number left 3*/ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(6), 7, 7, 4); | ||||
| 
 | ||||
| 	/* Clock accuracy and sample rate */ | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(7), | ||||
| 		cfg->iec60958_cfg->status[3]); | ||||
| 
 | ||||
| 	/* Original sample rate and word length */ | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDSCHNLS(8), | ||||
| 		cfg->iec60958_cfg->status[4]); | ||||
| 
 | ||||
| 	/* Enable FIFO empty and full interrupts */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_INT, 3, 3, 2); | ||||
| 
 | ||||
| 	/* Configure GPA */ | ||||
| 	/* select HBR/SPDIF interfaces */ | ||||
| 	if (cfg->layout == HDMI_AUDIO_LAYOUT_2CH) { | ||||
| 		/* select HBR/SPDIF interfaces */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5); | ||||
| 		/* enable two channels in GPA */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 3, 7, 0); | ||||
| 	} else if (cfg->layout == HDMI_AUDIO_LAYOUT_6CH) { | ||||
| 		/* select HBR/SPDIF interfaces */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5); | ||||
| 		/* enable six channels in GPA */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0x3F, 7, 0); | ||||
| 	} else { | ||||
| 		/* select HBR/SPDIF interfaces */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_CONF0, 0, 5, 5); | ||||
| 		/* enable eight channels in GPA */ | ||||
| 		REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF1, 0xFF, 7, 0); | ||||
| 	} | ||||
| 
 | ||||
| 	/* disable HBR */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 0, 0, 0); | ||||
| 	/* enable PCUV */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_GP_CONF2, 1, 1, 1); | ||||
| 	/* enable GPA FIFO full and empty mask */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_GP_MASK, 3, 1, 0); | ||||
| 	/* set polarity of GPA FIFO empty interrupts */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_AUD_GP_POL, 1, 0, 0); | ||||
| 
 | ||||
| 	/* unmute audio */ | ||||
| 	REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCONF, 0, 7, 4); | ||||
| } | ||||
| 
 | ||||
| static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core, | ||||
| 	 struct snd_cea_861_aud_if *info_aud) | ||||
| { | ||||
| 	void __iomem *base = core->base; | ||||
| 
 | ||||
| 	/* channel count and coding type fields in AUDICONF0 are swapped */ | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF0, | ||||
| 		(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) << 4 | | ||||
| 		(info_aud->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CT) >> 4); | ||||
| 
 | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss); | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca); | ||||
| 	hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3, info_aud->db5_dminh_lsv); | ||||
| } | ||||
| 
 | ||||
| int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 			struct omap_dss_audio *audio, u32 pclk) | ||||
| { | ||||
| 	struct hdmi_audio_format audio_format; | ||||
| 	struct hdmi_audio_dma audio_dma; | ||||
| 	struct hdmi_core_audio_config core_cfg; | ||||
| 	int err, n, cts, channel_count; | ||||
| 	unsigned int fs_nr; | ||||
| 	bool word_length_16b = false; | ||||
| 
 | ||||
| 	if (!audio || !audio->iec || !audio->cea || !core) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	core_cfg.iec60958_cfg = audio->iec; | ||||
| 
 | ||||
| 	if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) && | ||||
| 		(audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16)) | ||||
| 			word_length_16b = true; | ||||
| 
 | ||||
| 	/* only 16-bit word length supported atm */ | ||||
| 	if (!word_length_16b) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { | ||||
| 	case IEC958_AES3_CON_FS_32000: | ||||
| 		fs_nr = 32000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_44100: | ||||
| 		fs_nr = 44100; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_48000: | ||||
| 		fs_nr = 48000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_88200: | ||||
| 		fs_nr = 88200; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_96000: | ||||
| 		fs_nr = 96000; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_176400: | ||||
| 		fs_nr = 176400; | ||||
| 		break; | ||||
| 	case IEC958_AES3_CON_FS_192000: | ||||
| 		fs_nr = 192000; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	err = hdmi_compute_acr(pclk, fs_nr, &n, &cts); | ||||
| 	core_cfg.n = n; | ||||
| 	core_cfg.cts = cts; | ||||
| 
 | ||||
| 	/* Audio channels settings */ | ||||
| 	channel_count = (audio->cea->db1_ct_cc & CEA861_AUDIO_INFOFRAME_DB1CC) | ||||
| 				+ 1; | ||||
| 
 | ||||
| 	if (channel_count == 2) | ||||
| 		core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; | ||||
| 	else if (channel_count == 6) | ||||
| 		core_cfg.layout = HDMI_AUDIO_LAYOUT_6CH; | ||||
| 	else | ||||
| 		core_cfg.layout = HDMI_AUDIO_LAYOUT_8CH; | ||||
| 
 | ||||
| 	/* DMA settings */ | ||||
| 	if (word_length_16b) | ||||
| 		audio_dma.transfer_size = 0x10; | ||||
| 	else | ||||
| 		audio_dma.transfer_size = 0x20; | ||||
| 	audio_dma.block_size = 0xC0; | ||||
| 	audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; | ||||
| 	audio_dma.fifo_threshold = 0x20; /* in number of samples */ | ||||
| 
 | ||||
| 	/* audio FIFO format settings for 16-bit samples*/ | ||||
| 	audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; | ||||
| 	audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; | ||||
| 	audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; | ||||
| 
 | ||||
| 	/* only LPCM atm */ | ||||
| 	audio_format.type = HDMI_AUDIO_TYPE_LPCM; | ||||
| 
 | ||||
| 	/* disable start/stop signals of IEC 60958 blocks */ | ||||
| 	audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; | ||||
| 
 | ||||
| 	/* configure DMA and audio FIFO format*/ | ||||
| 	hdmi_wp_audio_config_dma(wp, &audio_dma); | ||||
| 	hdmi_wp_audio_config_format(wp, &audio_format); | ||||
| 
 | ||||
| 	/* configure the core */ | ||||
| 	hdmi5_core_audio_config(core, &core_cfg); | ||||
| 
 | ||||
| 	/* configure CEA 861 audio infoframe */ | ||||
| 	hdmi5_core_audio_infoframe_cfg(core, audio->cea); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | ||||
| 	if (!res) { | ||||
| 		DSSERR("can't get CORE IORESOURCE_MEM HDMI\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	core->base = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(core->base)) { | ||||
| 		DSSERR("can't ioremap HDMI core\n"); | ||||
| 		return PTR_ERR(core->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										306
									
								
								drivers/video/fbdev/omap2/dss/hdmi5_core.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								drivers/video/fbdev/omap2/dss/hdmi5_core.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | |||
| /*
 | ||||
|  * HDMI driver definition for TI OMAP5 processors. | ||||
|  * | ||||
|  * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/
 | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _HDMI5_CORE_H_ | ||||
| #define _HDMI5_CORE_H_ | ||||
| 
 | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| /* HDMI IP Core System */ | ||||
| 
 | ||||
| /* HDMI Identification */ | ||||
| #define HDMI_CORE_DESIGN_ID			0x00000 | ||||
| #define HDMI_CORE_REVISION_ID			0x00004 | ||||
| #define HDMI_CORE_PRODUCT_ID0			0x00008 | ||||
| #define HDMI_CORE_PRODUCT_ID1			0x0000C | ||||
| #define HDMI_CORE_CONFIG0_ID			0x00010 | ||||
| #define HDMI_CORE_CONFIG1_ID			0x00014 | ||||
| #define HDMI_CORE_CONFIG2_ID			0x00018 | ||||
| #define HDMI_CORE_CONFIG3_ID			0x0001C | ||||
| 
 | ||||
| /* HDMI Interrupt */ | ||||
| #define HDMI_CORE_IH_FC_STAT0			0x00400 | ||||
| #define HDMI_CORE_IH_FC_STAT1			0x00404 | ||||
| #define HDMI_CORE_IH_FC_STAT2			0x00408 | ||||
| #define HDMI_CORE_IH_AS_STAT0			0x0040C | ||||
| #define HDMI_CORE_IH_PHY_STAT0			0x00410 | ||||
| #define HDMI_CORE_IH_I2CM_STAT0			0x00414 | ||||
| #define HDMI_CORE_IH_CEC_STAT0			0x00418 | ||||
| #define HDMI_CORE_IH_VP_STAT0			0x0041C | ||||
| #define HDMI_CORE_IH_I2CMPHY_STAT0		0x00420 | ||||
| #define HDMI_CORE_IH_MUTE			0x007FC | ||||
| 
 | ||||
| /* HDMI Video Sampler */ | ||||
| #define HDMI_CORE_TX_INVID0			0x00800 | ||||
| #define HDMI_CORE_TX_INSTUFFING			0x00804 | ||||
| #define HDMI_CORE_TX_RGYDATA0			0x00808 | ||||
| #define HDMI_CORE_TX_RGYDATA1			0x0080C | ||||
| #define HDMI_CORE_TX_RCRDATA0			0x00810 | ||||
| #define HDMI_CORE_TX_RCRDATA1			0x00814 | ||||
| #define HDMI_CORE_TX_BCBDATA0			0x00818 | ||||
| #define HDMI_CORE_TX_BCBDATA1			0x0081C | ||||
| 
 | ||||
| /* HDMI Video Packetizer */ | ||||
| #define HDMI_CORE_VP_STATUS			0x02000 | ||||
| #define HDMI_CORE_VP_PR_CD			0x02004 | ||||
| #define HDMI_CORE_VP_STUFF			0x02008 | ||||
| #define HDMI_CORE_VP_REMAP			0x0200C | ||||
| #define HDMI_CORE_VP_CONF			0x02010 | ||||
| #define HDMI_CORE_VP_STAT			0x02014 | ||||
| #define HDMI_CORE_VP_INT			0x02018 | ||||
| #define HDMI_CORE_VP_MASK			0x0201C | ||||
| #define HDMI_CORE_VP_POL			0x02020 | ||||
| 
 | ||||
| /* Frame Composer */ | ||||
| #define HDMI_CORE_FC_INVIDCONF			0x04000 | ||||
| #define HDMI_CORE_FC_INHACTIV0			0x04004 | ||||
| #define HDMI_CORE_FC_INHACTIV1			0x04008 | ||||
| #define HDMI_CORE_FC_INHBLANK0			0x0400C | ||||
| #define HDMI_CORE_FC_INHBLANK1			0x04010 | ||||
| #define HDMI_CORE_FC_INVACTIV0			0x04014 | ||||
| #define HDMI_CORE_FC_INVACTIV1			0x04018 | ||||
| #define HDMI_CORE_FC_INVBLANK			0x0401C | ||||
| #define HDMI_CORE_FC_HSYNCINDELAY0		0x04020 | ||||
| #define HDMI_CORE_FC_HSYNCINDELAY1		0x04024 | ||||
| #define HDMI_CORE_FC_HSYNCINWIDTH0		0x04028 | ||||
| #define HDMI_CORE_FC_HSYNCINWIDTH1		0x0402C | ||||
| #define HDMI_CORE_FC_VSYNCINDELAY		0x04030 | ||||
| #define HDMI_CORE_FC_VSYNCINWIDTH		0x04034 | ||||
| #define HDMI_CORE_FC_INFREQ0			0x04038 | ||||
| #define HDMI_CORE_FC_INFREQ1			0x0403C | ||||
| #define HDMI_CORE_FC_INFREQ2			0x04040 | ||||
| #define HDMI_CORE_FC_CTRLDUR			0x04044 | ||||
| #define HDMI_CORE_FC_EXCTRLDUR			0x04048 | ||||
| #define HDMI_CORE_FC_EXCTRLSPAC			0x0404C | ||||
| #define HDMI_CORE_FC_CH0PREAM			0x04050 | ||||
| #define HDMI_CORE_FC_CH1PREAM			0x04054 | ||||
| #define HDMI_CORE_FC_CH2PREAM			0x04058 | ||||
| #define HDMI_CORE_FC_AVICONF3			0x0405C | ||||
| #define HDMI_CORE_FC_GCP			0x04060 | ||||
| #define HDMI_CORE_FC_AVICONF0			0x04064 | ||||
| #define HDMI_CORE_FC_AVICONF1			0x04068 | ||||
| #define HDMI_CORE_FC_AVICONF2			0x0406C | ||||
| #define HDMI_CORE_FC_AVIVID			0x04070 | ||||
| #define HDMI_CORE_FC_AVIETB0			0x04074 | ||||
| #define HDMI_CORE_FC_AVIETB1			0x04078 | ||||
| #define HDMI_CORE_FC_AVISBB0			0x0407C | ||||
| #define HDMI_CORE_FC_AVISBB1			0x04080 | ||||
| #define HDMI_CORE_FC_AVIELB0			0x04084 | ||||
| #define HDMI_CORE_FC_AVIELB1			0x04088 | ||||
| #define HDMI_CORE_FC_AVISRB0			0x0408C | ||||
| #define HDMI_CORE_FC_AVISRB1			0x04090 | ||||
| #define HDMI_CORE_FC_AUDICONF0			0x04094 | ||||
| #define HDMI_CORE_FC_AUDICONF1			0x04098 | ||||
| #define HDMI_CORE_FC_AUDICONF2			0x0409C | ||||
| #define HDMI_CORE_FC_AUDICONF3			0x040A0 | ||||
| #define HDMI_CORE_FC_VSDIEEEID0			0x040A4 | ||||
| #define HDMI_CORE_FC_VSDSIZE			0x040A8 | ||||
| #define HDMI_CORE_FC_VSDIEEEID1			0x040C0 | ||||
| #define HDMI_CORE_FC_VSDIEEEID2			0x040C4 | ||||
| #define HDMI_CORE_FC_VSDPAYLOAD(n)		(n * 4 + 0x040C8) | ||||
| #define HDMI_CORE_FC_SPDVENDORNAME(n)		(n * 4 + 0x04128) | ||||
| #define HDMI_CORE_FC_SPDPRODUCTNAME(n)		(n * 4 + 0x04148) | ||||
| #define HDMI_CORE_FC_SPDDEVICEINF		0x04188 | ||||
| #define HDMI_CORE_FC_AUDSCONF			0x0418C | ||||
| #define HDMI_CORE_FC_AUDSSTAT			0x04190 | ||||
| #define HDMI_CORE_FC_AUDSV			0x04194 | ||||
| #define HDMI_CORE_FC_AUDSU			0x04198 | ||||
| #define HDMI_CORE_FC_AUDSCHNLS(n)		(n * 4 + 0x0419C) | ||||
| #define HDMI_CORE_FC_CTRLQHIGH			0x041CC | ||||
| #define HDMI_CORE_FC_CTRLQLOW			0x041D0 | ||||
| #define HDMI_CORE_FC_ACP0			0x041D4 | ||||
| #define HDMI_CORE_FC_ACP(n)			((16-n) * 4 + 0x04208) | ||||
| #define HDMI_CORE_FC_ISCR1_0			0x04248 | ||||
| #define HDMI_CORE_FC_ISCR1(n)			((16-n) * 4 + 0x0424C) | ||||
| #define HDMI_CORE_FC_ISCR2(n)			((15-n) * 4 + 0x0428C) | ||||
| #define HDMI_CORE_FC_DATAUTO0			0x042CC | ||||
| #define HDMI_CORE_FC_DATAUTO1			0x042D0 | ||||
| #define HDMI_CORE_FC_DATAUTO2			0x042D4 | ||||
| #define HDMI_CORE_FC_DATMAN			0x042D8 | ||||
| #define HDMI_CORE_FC_DATAUTO3			0x042DC | ||||
| #define HDMI_CORE_FC_RDRB(n)			(n * 4 + 0x042E0) | ||||
| #define HDMI_CORE_FC_STAT0			0x04340 | ||||
| #define HDMI_CORE_FC_INT0			0x04344 | ||||
| #define HDMI_CORE_FC_MASK0			0x04348 | ||||
| #define HDMI_CORE_FC_POL0			0x0434C | ||||
| #define HDMI_CORE_FC_STAT1			0x04350 | ||||
| #define HDMI_CORE_FC_INT1			0x04354 | ||||
| #define HDMI_CORE_FC_MASK1			0x04358 | ||||
| #define HDMI_CORE_FC_POL1			0x0435C | ||||
| #define HDMI_CORE_FC_STAT2			0x04360 | ||||
| #define HDMI_CORE_FC_INT2			0x04364 | ||||
| #define HDMI_CORE_FC_MASK2			0x04368 | ||||
| #define HDMI_CORE_FC_POL2			0x0436C | ||||
| #define HDMI_CORE_FC_PRCONF			0x04380 | ||||
| #define HDMI_CORE_FC_GMD_STAT			0x04400 | ||||
| #define HDMI_CORE_FC_GMD_EN			0x04404 | ||||
| #define HDMI_CORE_FC_GMD_UP			0x04408 | ||||
| #define HDMI_CORE_FC_GMD_CONF			0x0440C | ||||
| #define HDMI_CORE_FC_GMD_HB			0x04410 | ||||
| #define HDMI_CORE_FC_GMD_PB(n)			(n * 4 + 0x04414) | ||||
| #define HDMI_CORE_FC_DBGFORCE			0x04800 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH0			0x04804 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH0			0x04808 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH0			0x0480C | ||||
| #define HDMI_CORE_FC_DBGAUD0CH1			0x04810 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH1			0x04814 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH1			0x04818 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH2			0x0481C | ||||
| #define HDMI_CORE_FC_DBGAUD1CH2			0x04820 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH2			0x04824 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH3			0x04828 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH3			0x0482C | ||||
| #define HDMI_CORE_FC_DBGAUD2CH3			0x04830 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH4			0x04834 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH4			0x04838 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH4			0x0483C | ||||
| #define HDMI_CORE_FC_DBGAUD0CH5			0x04840 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH5			0x04844 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH5			0x04848 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH6			0x0484C | ||||
| #define HDMI_CORE_FC_DBGAUD1CH6			0x04850 | ||||
| #define HDMI_CORE_FC_DBGAUD2CH6			0x04854 | ||||
| #define HDMI_CORE_FC_DBGAUD0CH7			0x04858 | ||||
| #define HDMI_CORE_FC_DBGAUD1CH7			0x0485C | ||||
| #define HDMI_CORE_FC_DBGAUD2CH7			0x04860 | ||||
| #define HDMI_CORE_FC_DBGTMDS0			0x04864 | ||||
| #define HDMI_CORE_FC_DBGTMDS1			0x04868 | ||||
| #define HDMI_CORE_FC_DBGTMDS2			0x0486C | ||||
| #define HDMI_CORE_PHY_MASK0			0x0C018 | ||||
| #define HDMI_CORE_PHY_I2CM_INT_ADDR		0x0C09C | ||||
| #define HDMI_CORE_PHY_I2CM_CTLINT_ADDR		0x0C0A0 | ||||
| 
 | ||||
| /* HDMI Audio */ | ||||
| #define HDMI_CORE_AUD_CONF0			0x0C400 | ||||
| #define HDMI_CORE_AUD_CONF1			0x0C404 | ||||
| #define HDMI_CORE_AUD_INT			0x0C408 | ||||
| #define HDMI_CORE_AUD_N1			0x0C800 | ||||
| #define HDMI_CORE_AUD_N2			0x0C804 | ||||
| #define HDMI_CORE_AUD_N3			0x0C808 | ||||
| #define HDMI_CORE_AUD_CTS1			0x0C80C | ||||
| #define HDMI_CORE_AUD_CTS2			0x0C810 | ||||
| #define HDMI_CORE_AUD_CTS3			0x0C814 | ||||
| #define HDMI_CORE_AUD_INCLKFS			0x0C818 | ||||
| #define HDMI_CORE_AUD_CC08			0x0CC08 | ||||
| #define HDMI_CORE_AUD_GP_CONF0			0x0D400 | ||||
| #define HDMI_CORE_AUD_GP_CONF1			0x0D404 | ||||
| #define HDMI_CORE_AUD_GP_CONF2			0x0D408 | ||||
| #define HDMI_CORE_AUD_D010			0x0D010 | ||||
| #define HDMI_CORE_AUD_GP_STAT			0x0D40C | ||||
| #define HDMI_CORE_AUD_GP_INT			0x0D410 | ||||
| #define HDMI_CORE_AUD_GP_POL			0x0D414 | ||||
| #define HDMI_CORE_AUD_GP_MASK			0x0D418 | ||||
| 
 | ||||
| /* HDMI Main Controller */ | ||||
| #define HDMI_CORE_MC_CLKDIS			0x10004 | ||||
| #define HDMI_CORE_MC_SWRSTZREQ			0x10008 | ||||
| #define HDMI_CORE_MC_FLOWCTRL			0x10010 | ||||
| #define HDMI_CORE_MC_PHYRSTZ			0x10014 | ||||
| #define HDMI_CORE_MC_LOCKONCLOCK		0x10018 | ||||
| 
 | ||||
| /* HDMI COLOR SPACE CONVERTER */ | ||||
| #define HDMI_CORE_CSC_CFG			0x10400 | ||||
| #define HDMI_CORE_CSC_SCALE			0x10404 | ||||
| #define HDMI_CORE_CSC_COEF_A1_MSB		0x10408 | ||||
| #define HDMI_CORE_CSC_COEF_A1_LSB		0x1040C | ||||
| #define HDMI_CORE_CSC_COEF_A2_MSB		0x10410 | ||||
| #define HDMI_CORE_CSC_COEF_A2_LSB		0x10414 | ||||
| #define HDMI_CORE_CSC_COEF_A3_MSB		0x10418 | ||||
| #define HDMI_CORE_CSC_COEF_A3_LSB		0x1041C | ||||
| #define HDMI_CORE_CSC_COEF_A4_MSB		0x10420 | ||||
| #define HDMI_CORE_CSC_COEF_A4_LSB		0x10424 | ||||
| #define HDMI_CORE_CSC_COEF_B1_MSB		0x10428 | ||||
| #define HDMI_CORE_CSC_COEF_B1_LSB		0x1042C | ||||
| #define HDMI_CORE_CSC_COEF_B2_MSB		0x10430 | ||||
| #define HDMI_CORE_CSC_COEF_B2_LSB		0x10434 | ||||
| #define HDMI_CORE_CSC_COEF_B3_MSB		0x10438 | ||||
| #define HDMI_CORE_CSC_COEF_B3_LSB		0x1043C | ||||
| #define HDMI_CORE_CSC_COEF_B4_MSB		0x10440 | ||||
| #define HDMI_CORE_CSC_COEF_B4_LSB		0x10444 | ||||
| #define HDMI_CORE_CSC_COEF_C1_MSB		0x10448 | ||||
| #define HDMI_CORE_CSC_COEF_C1_LSB		0x1044C | ||||
| #define HDMI_CORE_CSC_COEF_C2_MSB		0x10450 | ||||
| #define HDMI_CORE_CSC_COEF_C2_LSB		0x10454 | ||||
| #define HDMI_CORE_CSC_COEF_C3_MSB		0x10458 | ||||
| #define HDMI_CORE_CSC_COEF_C3_LSB		0x1045C | ||||
| #define HDMI_CORE_CSC_COEF_C4_MSB		0x10460 | ||||
| #define HDMI_CORE_CSC_COEF_C4_LSB		0x10464 | ||||
| 
 | ||||
| /* HDMI HDCP */ | ||||
| #define HDMI_CORE_HDCP_MASK			0x14020 | ||||
| 
 | ||||
| /* HDMI CEC */ | ||||
| #define HDMI_CORE_CEC_MASK			0x17408 | ||||
| 
 | ||||
| /* HDMI I2C Master */ | ||||
| #define HDMI_CORE_I2CM_SLAVE			0x157C8 | ||||
| #define HDMI_CORE_I2CM_ADDRESS			0x157CC | ||||
| #define HDMI_CORE_I2CM_DATAO			0x157D0 | ||||
| #define HDMI_CORE_I2CM_DATAI			0X157D4 | ||||
| #define HDMI_CORE_I2CM_OPERATION		0x157D8 | ||||
| #define HDMI_CORE_I2CM_INT			0x157DC | ||||
| #define HDMI_CORE_I2CM_CTLINT			0x157E0 | ||||
| #define HDMI_CORE_I2CM_DIV			0x157E4 | ||||
| #define HDMI_CORE_I2CM_SEGADDR			0x157E8 | ||||
| #define HDMI_CORE_I2CM_SOFTRSTZ			0x157EC | ||||
| #define HDMI_CORE_I2CM_SEGPTR			0x157F0 | ||||
| #define HDMI_CORE_I2CM_SS_SCL_HCNT_1_ADDR	0x157F4 | ||||
| #define HDMI_CORE_I2CM_SS_SCL_HCNT_0_ADDR	0x157F8 | ||||
| #define HDMI_CORE_I2CM_SS_SCL_LCNT_1_ADDR	0x157FC | ||||
| #define HDMI_CORE_I2CM_SS_SCL_LCNT_0_ADDR	0x15800 | ||||
| #define HDMI_CORE_I2CM_FS_SCL_HCNT_1_ADDR	0x15804 | ||||
| #define HDMI_CORE_I2CM_FS_SCL_HCNT_0_ADDR	0x15808 | ||||
| #define HDMI_CORE_I2CM_FS_SCL_LCNT_1_ADDR	0x1580C | ||||
| #define HDMI_CORE_I2CM_FS_SCL_LCNT_0_ADDR	0x15810 | ||||
| #define HDMI_CORE_I2CM_SDA_HOLD_ADDR		0x15814 | ||||
| 
 | ||||
| enum hdmi_core_packet_mode { | ||||
| 	HDMI_PACKETMODERESERVEDVALUE = 0, | ||||
| 	HDMI_PACKETMODE24BITPERPIXEL = 4, | ||||
| 	HDMI_PACKETMODE30BITPERPIXEL = 5, | ||||
| 	HDMI_PACKETMODE36BITPERPIXEL = 6, | ||||
| 	HDMI_PACKETMODE48BITPERPIXEL = 7, | ||||
| }; | ||||
| 
 | ||||
| struct hdmi_core_vid_config { | ||||
| 	struct hdmi_config v_fc_config; | ||||
| 	enum hdmi_core_packet_mode packet_mode; | ||||
| 	int data_enable_pol; | ||||
| 	int vblank_osc; | ||||
| 	int hblank; | ||||
| 	int vblank; | ||||
| }; | ||||
| 
 | ||||
| struct csc_table { | ||||
| 	u16 a1, a2, a3, a4; | ||||
| 	u16 b1, b2, b3, b4; | ||||
| 	u16 c1, c2, c3, c4; | ||||
| }; | ||||
| 
 | ||||
| int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len); | ||||
| void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s); | ||||
| int hdmi5_core_handle_irqs(struct hdmi_core_data *core); | ||||
| void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 			struct hdmi_config *cfg); | ||||
| int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core); | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, | ||||
| 			struct omap_dss_audio *audio, u32 pclk); | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										150
									
								
								drivers/video/fbdev/omap2/dss/hdmi_common.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								drivers/video/fbdev/omap2/dss/hdmi_common.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | |||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMI" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/of.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep, | ||||
| 	struct hdmi_phy_data *phy) | ||||
| { | ||||
| 	struct property *prop; | ||||
| 	int r, len; | ||||
| 
 | ||||
| 	prop = of_find_property(ep, "lanes", &len); | ||||
| 	if (prop) { | ||||
| 		u32 lanes[8]; | ||||
| 
 | ||||
| 		if (len / sizeof(u32) != ARRAY_SIZE(lanes)) { | ||||
| 			dev_err(&pdev->dev, "bad number of lanes\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		r = of_property_read_u32_array(ep, "lanes", lanes, | ||||
| 			ARRAY_SIZE(lanes)); | ||||
| 		if (r) { | ||||
| 			dev_err(&pdev->dev, "failed to read lane data\n"); | ||||
| 			return r; | ||||
| 		} | ||||
| 
 | ||||
| 		r = hdmi_phy_parse_lanes(phy, lanes); | ||||
| 		if (r) { | ||||
| 			dev_err(&pdev->dev, "failed to parse lane data\n"); | ||||
| 			return r; | ||||
| 		} | ||||
| 	} else { | ||||
| 		static const u32 default_lanes[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; | ||||
| 
 | ||||
| 		r = hdmi_phy_parse_lanes(phy, default_lanes); | ||||
| 		if (WARN_ON(r)) { | ||||
| 			dev_err(&pdev->dev, "failed to parse lane data\n"); | ||||
| 			return r; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||||
| int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts) | ||||
| { | ||||
| 	u32 deep_color; | ||||
| 	bool deep_color_correct = false; | ||||
| 
 | ||||
| 	if (n == NULL || cts == NULL) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* TODO: When implemented, query deep color mode here. */ | ||||
| 	deep_color = 100; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * When using deep color, the default N value (as in the HDMI | ||||
| 	 * specification) yields to an non-integer CTS. Hence, we | ||||
| 	 * modify it while keeping the restrictions described in | ||||
| 	 * section 7.2.1 of the HDMI 1.4a specification. | ||||
| 	 */ | ||||
| 	switch (sample_freq) { | ||||
| 	case 32000: | ||||
| 	case 48000: | ||||
| 	case 96000: | ||||
| 	case 192000: | ||||
| 		if (deep_color == 125) | ||||
| 			if (pclk == 27027000 || pclk == 74250000) | ||||
| 				deep_color_correct = true; | ||||
| 		if (deep_color == 150) | ||||
| 			if (pclk == 27027000) | ||||
| 				deep_color_correct = true; | ||||
| 		break; | ||||
| 	case 44100: | ||||
| 	case 88200: | ||||
| 	case 176400: | ||||
| 		if (deep_color == 125) | ||||
| 			if (pclk == 27027000) | ||||
| 				deep_color_correct = true; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (deep_color_correct) { | ||||
| 		switch (sample_freq) { | ||||
| 		case 32000: | ||||
| 			*n = 8192; | ||||
| 			break; | ||||
| 		case 44100: | ||||
| 			*n = 12544; | ||||
| 			break; | ||||
| 		case 48000: | ||||
| 			*n = 8192; | ||||
| 			break; | ||||
| 		case 88200: | ||||
| 			*n = 25088; | ||||
| 			break; | ||||
| 		case 96000: | ||||
| 			*n = 16384; | ||||
| 			break; | ||||
| 		case 176400: | ||||
| 			*n = 50176; | ||||
| 			break; | ||||
| 		case 192000: | ||||
| 			*n = 32768; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} else { | ||||
| 		switch (sample_freq) { | ||||
| 		case 32000: | ||||
| 			*n = 4096; | ||||
| 			break; | ||||
| 		case 44100: | ||||
| 			*n = 6272; | ||||
| 			break; | ||||
| 		case 48000: | ||||
| 			*n = 6144; | ||||
| 			break; | ||||
| 		case 88200: | ||||
| 			*n = 12544; | ||||
| 			break; | ||||
| 		case 96000: | ||||
| 			*n = 12288; | ||||
| 			break; | ||||
| 		case 176400: | ||||
| 			*n = 25088; | ||||
| 			break; | ||||
| 		case 192000: | ||||
| 			*n = 24576; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ | ||||
| 	*cts = (pclk/1000) * (*n / 128) * deep_color / (sample_freq / 10); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										255
									
								
								drivers/video/fbdev/omap2/dss/hdmi_phy.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								drivers/video/fbdev/omap2/dss/hdmi_phy.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,255 @@ | |||
| /*
 | ||||
|  * HDMI PHY | ||||
|  * | ||||
|  * Copyright (C) 2013 Texas Instruments Incorporated | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/slab.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| struct hdmi_phy_features { | ||||
| 	bool bist_ctrl; | ||||
| 	bool calc_freqout; | ||||
| 	bool ldo_voltage; | ||||
| 	unsigned long dcofreq_min; | ||||
| 	unsigned long max_phy; | ||||
| }; | ||||
| 
 | ||||
| static const struct hdmi_phy_features *phy_feat; | ||||
| 
 | ||||
| void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) | ||||
| { | ||||
| #define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ | ||||
| 		hdmi_read_reg(phy->base, r)) | ||||
| 
 | ||||
| 	DUMPPHY(HDMI_TXPHY_TX_CTRL); | ||||
| 	DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); | ||||
| 	DUMPPHY(HDMI_TXPHY_POWER_CTRL); | ||||
| 	DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); | ||||
| 	if (phy_feat->bist_ctrl) | ||||
| 		DUMPPHY(HDMI_TXPHY_BIST_CONTROL); | ||||
| } | ||||
| 
 | ||||
| int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < 8; i += 2) { | ||||
| 		u8 lane, pol; | ||||
| 		int dx, dy; | ||||
| 
 | ||||
| 		dx = lanes[i]; | ||||
| 		dy = lanes[i + 1]; | ||||
| 
 | ||||
| 		if (dx < 0 || dx >= 8) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		if (dy < 0 || dy >= 8) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		if (dx & 1) { | ||||
| 			if (dy != dx - 1) | ||||
| 				return -EINVAL; | ||||
| 			pol = 1; | ||||
| 		} else { | ||||
| 			if (dy != dx + 1) | ||||
| 				return -EINVAL; | ||||
| 			pol = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		lane = dx / 2; | ||||
| 
 | ||||
| 		phy->lane_function[lane] = i / 2; | ||||
| 		phy->lane_polarity[lane] = pol; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) | ||||
| { | ||||
| 	static const u16 pad_cfg_list[] = { | ||||
| 		0x0123, | ||||
| 		0x0132, | ||||
| 		0x0312, | ||||
| 		0x0321, | ||||
| 		0x0231, | ||||
| 		0x0213, | ||||
| 		0x1023, | ||||
| 		0x1032, | ||||
| 		0x3012, | ||||
| 		0x3021, | ||||
| 		0x2031, | ||||
| 		0x2013, | ||||
| 		0x1203, | ||||
| 		0x1302, | ||||
| 		0x3102, | ||||
| 		0x3201, | ||||
| 		0x2301, | ||||
| 		0x2103, | ||||
| 		0x1230, | ||||
| 		0x1320, | ||||
| 		0x3120, | ||||
| 		0x3210, | ||||
| 		0x2310, | ||||
| 		0x2130, | ||||
| 	}; | ||||
| 
 | ||||
| 	u16 lane_cfg = 0; | ||||
| 	int i; | ||||
| 	unsigned lane_cfg_val; | ||||
| 	u16 pol_val = 0; | ||||
| 
 | ||||
| 	for (i = 0; i < 4; ++i) | ||||
| 		lane_cfg |= phy->lane_function[i] << ((3 - i) * 4); | ||||
| 
 | ||||
| 	pol_val |= phy->lane_polarity[0] << 0; | ||||
| 	pol_val |= phy->lane_polarity[1] << 3; | ||||
| 	pol_val |= phy->lane_polarity[2] << 2; | ||||
| 	pol_val |= phy->lane_polarity[3] << 1; | ||||
| 
 | ||||
| 	for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i) | ||||
| 		if (pad_cfg_list[i] == lane_cfg) | ||||
| 			break; | ||||
| 
 | ||||
| 	if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list))) | ||||
| 		i = 0; | ||||
| 
 | ||||
| 	lane_cfg_val = i; | ||||
| 
 | ||||
| 	REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22); | ||||
| 	REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); | ||||
| } | ||||
| 
 | ||||
| int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) | ||||
| { | ||||
| 	u8 freqout; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Read address 0 in order to get the SCP reset done completed | ||||
| 	 * Dummy access performed to make sure reset is done | ||||
| 	 */ | ||||
| 	hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the | ||||
| 	 * HDMI_PHYPWRCMD_LDOON command. | ||||
| 	*/ | ||||
| 	if (phy_feat->bist_ctrl) | ||||
| 		REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); | ||||
| 
 | ||||
| 	if (phy_feat->calc_freqout) { | ||||
| 		/* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */ | ||||
| 		u32 dco_min = phy_feat->dcofreq_min / 10; | ||||
| 		u32 pclk = cfg->timings.pixelclock; | ||||
| 
 | ||||
| 		if (pclk < dco_min) | ||||
| 			freqout = 0; | ||||
| 		else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy)) | ||||
| 			freqout = 1; | ||||
| 		else | ||||
| 			freqout = 2; | ||||
| 	} else { | ||||
| 		freqout = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Write to phy address 0 to configure the clock | ||||
| 	 * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field | ||||
| 	 */ | ||||
| 	REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30); | ||||
| 
 | ||||
| 	/* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ | ||||
| 	hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); | ||||
| 
 | ||||
| 	/* Setup max LDO voltage */ | ||||
| 	if (phy_feat->ldo_voltage) | ||||
| 		REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); | ||||
| 
 | ||||
| 	hdmi_phy_configure_lanes(phy); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct hdmi_phy_features omap44xx_phy_feats = { | ||||
| 	.bist_ctrl	=	false, | ||||
| 	.calc_freqout	=	false, | ||||
| 	.ldo_voltage	=	true, | ||||
| 	.dcofreq_min	=	500000000, | ||||
| 	.max_phy	=	185675000, | ||||
| }; | ||||
| 
 | ||||
| static const struct hdmi_phy_features omap54xx_phy_feats = { | ||||
| 	.bist_ctrl	=	true, | ||||
| 	.calc_freqout	=	true, | ||||
| 	.ldo_voltage	=	false, | ||||
| 	.dcofreq_min	=	750000000, | ||||
| 	.max_phy	=	186000000, | ||||
| }; | ||||
| 
 | ||||
| static int hdmi_phy_init_features(struct platform_device *pdev) | ||||
| { | ||||
| 	struct hdmi_phy_features *dst; | ||||
| 	const struct hdmi_phy_features *src; | ||||
| 
 | ||||
| 	dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); | ||||
| 	if (!dst) { | ||||
| 		dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (omapdss_get_version()) { | ||||
| 	case OMAPDSS_VER_OMAP4430_ES1: | ||||
| 	case OMAPDSS_VER_OMAP4430_ES2: | ||||
| 	case OMAPDSS_VER_OMAP4: | ||||
| 		src = &omap44xx_phy_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP5: | ||||
| 		src = &omap54xx_phy_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(dst, src, sizeof(*dst)); | ||||
| 	phy_feat = dst; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy) | ||||
| { | ||||
| 	int r; | ||||
| 	struct resource *res; | ||||
| 
 | ||||
| 	r = hdmi_phy_init_features(pdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); | ||||
| 	if (!res) { | ||||
| 		DSSERR("can't get PHY mem resource\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	phy->base = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(phy->base)) { | ||||
| 		DSSERR("can't ioremap TX PHY\n"); | ||||
| 		return PTR_ERR(phy->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										290
									
								
								drivers/video/fbdev/omap2/dss/hdmi_pll.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										290
									
								
								drivers/video/fbdev/omap2/dss/hdmi_pll.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,290 @@ | |||
| /*
 | ||||
|  * HDMI PLL | ||||
|  * | ||||
|  * Copyright (C) 2013 Texas Instruments Incorporated | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMIPLL" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| #define HDMI_DEFAULT_REGN 16 | ||||
| #define HDMI_DEFAULT_REGM2 1 | ||||
| 
 | ||||
| struct hdmi_pll_features { | ||||
| 	bool sys_reset; | ||||
| 	/* this is a hack, need to replace it with a better computation of M2 */ | ||||
| 	bool bound_dcofreq; | ||||
| 	unsigned long fint_min, fint_max; | ||||
| 	u16 regm_max; | ||||
| 	unsigned long dcofreq_low_min, dcofreq_low_max; | ||||
| 	unsigned long dcofreq_high_min, dcofreq_high_max; | ||||
| }; | ||||
| 
 | ||||
| static const struct hdmi_pll_features *pll_feat; | ||||
| 
 | ||||
| void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) | ||||
| { | ||||
| #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ | ||||
| 		hdmi_read_reg(pll->base, r)) | ||||
| 
 | ||||
| 	DUMPPLL(PLLCTRL_PLL_CONTROL); | ||||
| 	DUMPPLL(PLLCTRL_PLL_STATUS); | ||||
| 	DUMPPLL(PLLCTRL_PLL_GO); | ||||
| 	DUMPPLL(PLLCTRL_CFG1); | ||||
| 	DUMPPLL(PLLCTRL_CFG2); | ||||
| 	DUMPPLL(PLLCTRL_CFG3); | ||||
| 	DUMPPLL(PLLCTRL_SSC_CFG1); | ||||
| 	DUMPPLL(PLLCTRL_SSC_CFG2); | ||||
| 	DUMPPLL(PLLCTRL_CFG4); | ||||
| } | ||||
| 
 | ||||
| void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) | ||||
| { | ||||
| 	struct hdmi_pll_info *pi = &pll->info; | ||||
| 	unsigned long refclk; | ||||
| 	u32 mf; | ||||
| 
 | ||||
| 	/* use our funky units */ | ||||
| 	clkin /= 10000; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Input clock is predivided by N + 1 | ||||
| 	 * out put of which is reference clk | ||||
| 	 */ | ||||
| 
 | ||||
| 	pi->regn = HDMI_DEFAULT_REGN; | ||||
| 
 | ||||
| 	refclk = clkin / pi->regn; | ||||
| 
 | ||||
| 	/* temorary hack to make sure DCO freq isn't calculated too low */ | ||||
| 	if (pll_feat->bound_dcofreq && phy <= 65000) | ||||
| 		pi->regm2 = 3; | ||||
| 	else | ||||
| 		pi->regm2 = HDMI_DEFAULT_REGM2; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * multiplier is pixel_clk/ref_clk | ||||
| 	 * Multiplying by 100 to avoid fractional part removal | ||||
| 	 */ | ||||
| 	pi->regm = phy * pi->regm2 / refclk; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * fractional multiplier is remainder of the difference between | ||||
| 	 * multiplier and actual phy(required pixel clock thus should be | ||||
| 	 * multiplied by 2^18(262144) divided by the reference clock | ||||
| 	 */ | ||||
| 	mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; | ||||
| 	pi->regmf = pi->regm2 * mf / refclk; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Dcofreq should be set to 1 if required pixel clock | ||||
| 	 * is greater than 1000MHz | ||||
| 	 */ | ||||
| 	pi->dcofreq = phy > 1000 * 100; | ||||
| 	pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10; | ||||
| 
 | ||||
| 	/* Set the reference clock to sysclk reference */ | ||||
| 	pi->refsel = HDMI_REFSEL_SYSCLK; | ||||
| 
 | ||||
| 	DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); | ||||
| 	DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static int hdmi_pll_config(struct hdmi_pll_data *pll) | ||||
| { | ||||
| 	u32 r; | ||||
| 	struct hdmi_pll_info *fmt = &pll->info; | ||||
| 
 | ||||
| 	/* PLL start always use manual mode */ | ||||
| 	REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); | ||||
| 	r = FLD_MOD(r, fmt->regm, 20, 9);	/* CFG1_PLL_REGM */ | ||||
| 	r = FLD_MOD(r, fmt->regn - 1, 8, 1);	/* CFG1_PLL_REGN */ | ||||
| 	hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); | ||||
| 
 | ||||
| 	r = FLD_MOD(r, 0x0, 12, 12);	/* PLL_HIGHFREQ divide by 2 */ | ||||
| 	r = FLD_MOD(r, 0x1, 13, 13);	/* PLL_REFEN */ | ||||
| 	r = FLD_MOD(r, 0x0, 14, 14);	/* PHY_CLKINEN de-assert during locking */ | ||||
| 	r = FLD_MOD(r, fmt->refsel, 22, 21);	/* REFSEL */ | ||||
| 
 | ||||
| 	if (fmt->dcofreq) | ||||
| 		r = FLD_MOD(r, 0x4, 3, 1);	/* 1000MHz and 2000MHz */ | ||||
| 	else | ||||
| 		r = FLD_MOD(r, 0x2, 3, 1);	/* 500MHz and 1000MHz */ | ||||
| 
 | ||||
| 	hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); | ||||
| 
 | ||||
| 	REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); | ||||
| 	r = FLD_MOD(r, fmt->regm2, 24, 18); | ||||
| 	r = FLD_MOD(r, fmt->regmf, 17, 0); | ||||
| 	hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); | ||||
| 
 | ||||
| 	/* go now */ | ||||
| 	REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); | ||||
| 
 | ||||
| 	/* wait for bit change */ | ||||
| 	if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, | ||||
| 			0, 0, 0) != 0) { | ||||
| 		DSSERR("PLL GO bit not clearing\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Wait till the lock bit is set in PLL status */ | ||||
| 	if (hdmi_wait_for_bit_change(pll->base, | ||||
| 			PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { | ||||
| 		DSSERR("cannot lock PLL\n"); | ||||
| 		DSSERR("CFG1 0x%x\n", | ||||
| 			hdmi_read_reg(pll->base, PLLCTRL_CFG1)); | ||||
| 		DSSERR("CFG2 0x%x\n", | ||||
| 			hdmi_read_reg(pll->base, PLLCTRL_CFG2)); | ||||
| 		DSSERR("CFG4 0x%x\n", | ||||
| 			hdmi_read_reg(pll->base, PLLCTRL_CFG4)); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	DSSDBG("PLL locked!\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int hdmi_pll_reset(struct hdmi_pll_data *pll) | ||||
| { | ||||
| 	/* SYSRESET  controlled by power FSM */ | ||||
| 	REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3); | ||||
| 
 | ||||
| 	/* READ 0x0 reset is in progress */ | ||||
| 	if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1) | ||||
| 			!= 1) { | ||||
| 		DSSERR("Failed to sysreset PLL\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	u16 r = 0; | ||||
| 
 | ||||
| 	r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_pll_reset(pll); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = hdmi_pll_config(pll); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | ||||
| } | ||||
| 
 | ||||
| static const struct hdmi_pll_features omap44xx_pll_feats = { | ||||
| 	.sys_reset		=	false, | ||||
| 	.bound_dcofreq		=	false, | ||||
| 	.fint_min		=	500000, | ||||
| 	.fint_max		=	2500000, | ||||
| 	.regm_max		=	4095, | ||||
| 	.dcofreq_low_min	=	500000000, | ||||
| 	.dcofreq_low_max	=	1000000000, | ||||
| 	.dcofreq_high_min	=	1000000000, | ||||
| 	.dcofreq_high_max	=	2000000000, | ||||
| }; | ||||
| 
 | ||||
| static const struct hdmi_pll_features omap54xx_pll_feats = { | ||||
| 	.sys_reset		=	true, | ||||
| 	.bound_dcofreq		=	true, | ||||
| 	.fint_min		=	620000, | ||||
| 	.fint_max		=	2500000, | ||||
| 	.regm_max		=	2046, | ||||
| 	.dcofreq_low_min	=	750000000, | ||||
| 	.dcofreq_low_max	=	1500000000, | ||||
| 	.dcofreq_high_min	=	1250000000, | ||||
| 	.dcofreq_high_max	=	2500000000UL, | ||||
| }; | ||||
| 
 | ||||
| static int hdmi_pll_init_features(struct platform_device *pdev) | ||||
| { | ||||
| 	struct hdmi_pll_features *dst; | ||||
| 	const struct hdmi_pll_features *src; | ||||
| 
 | ||||
| 	dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); | ||||
| 	if (!dst) { | ||||
| 		dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (omapdss_get_version()) { | ||||
| 	case OMAPDSS_VER_OMAP4430_ES1: | ||||
| 	case OMAPDSS_VER_OMAP4430_ES2: | ||||
| 	case OMAPDSS_VER_OMAP4: | ||||
| 		src = &omap44xx_pll_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	case OMAPDSS_VER_OMAP5: | ||||
| 		src = &omap54xx_pll_feats; | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	memcpy(dst, src, sizeof(*dst)); | ||||
| 	pll_feat = dst; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) | ||||
| { | ||||
| 	int r; | ||||
| 	struct resource *res; | ||||
| 
 | ||||
| 	r = hdmi_pll_init_features(pdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); | ||||
| 	if (!res) { | ||||
| 		DSSERR("can't get PLL mem resource\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	pll->base = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(pll->base)) { | ||||
| 		DSSERR("can't ioremap PLLCTRL\n"); | ||||
| 		return PTR_ERR(pll->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										258
									
								
								drivers/video/fbdev/omap2/dss/hdmi_wp.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								drivers/video/fbdev/omap2/dss/hdmi_wp.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,258 @@ | |||
| /*
 | ||||
|  * HDMI wrapper | ||||
|  * | ||||
|  * Copyright (C) 2013 Texas Instruments Incorporated | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "HDMIWP" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "hdmi.h" | ||||
| 
 | ||||
| void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s) | ||||
| { | ||||
| #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r)) | ||||
| 
 | ||||
| 	DUMPREG(HDMI_WP_REVISION); | ||||
| 	DUMPREG(HDMI_WP_SYSCONFIG); | ||||
| 	DUMPREG(HDMI_WP_IRQSTATUS_RAW); | ||||
| 	DUMPREG(HDMI_WP_IRQSTATUS); | ||||
| 	DUMPREG(HDMI_WP_IRQENABLE_SET); | ||||
| 	DUMPREG(HDMI_WP_IRQENABLE_CLR); | ||||
| 	DUMPREG(HDMI_WP_IRQWAKEEN); | ||||
| 	DUMPREG(HDMI_WP_PWR_CTRL); | ||||
| 	DUMPREG(HDMI_WP_DEBOUNCE); | ||||
| 	DUMPREG(HDMI_WP_VIDEO_CFG); | ||||
| 	DUMPREG(HDMI_WP_VIDEO_SIZE); | ||||
| 	DUMPREG(HDMI_WP_VIDEO_TIMING_H); | ||||
| 	DUMPREG(HDMI_WP_VIDEO_TIMING_V); | ||||
| 	DUMPREG(HDMI_WP_CLK); | ||||
| 	DUMPREG(HDMI_WP_AUDIO_CFG); | ||||
| 	DUMPREG(HDMI_WP_AUDIO_CFG2); | ||||
| 	DUMPREG(HDMI_WP_AUDIO_CTRL); | ||||
| 	DUMPREG(HDMI_WP_AUDIO_DATA); | ||||
| } | ||||
| 
 | ||||
| u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus) | ||||
| { | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus); | ||||
| 	/* flush posted write */ | ||||
| 	hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask) | ||||
| { | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask) | ||||
| { | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask); | ||||
| } | ||||
| 
 | ||||
| /* PHY_PWR_CMD */ | ||||
| int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val) | ||||
| { | ||||
| 	/* Return if already the state */ | ||||
| 	if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Command for power control of HDMI PHY */ | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6); | ||||
| 
 | ||||
| 	/* Status of the power control of HDMI PHY */ | ||||
| 	if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val) | ||||
| 			!= val) { | ||||
| 		DSSERR("Failed to set PHY power mode to %d\n", val); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* PLL_PWR_CMD */ | ||||
| int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val) | ||||
| { | ||||
| 	/* Command for power control of HDMI PLL */ | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2); | ||||
| 
 | ||||
| 	/* wait till PHY_PWR_STATUS is set */ | ||||
| 	if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val) | ||||
| 			!= val) { | ||||
| 		DSSERR("Failed to set PLL_PWR_STATUS\n"); | ||||
| 		return -ETIMEDOUT; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi_wp_video_start(struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_video_stop(struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_video_format *video_fmt) | ||||
| { | ||||
| 	u32 l = 0; | ||||
| 
 | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, | ||||
| 		10, 8); | ||||
| 
 | ||||
| 	l |= FLD_VAL(video_fmt->y_res, 31, 16); | ||||
| 	l |= FLD_VAL(video_fmt->x_res, 15, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	u32 r; | ||||
| 	bool vsync_pol, hsync_pol; | ||||
| 	DSSDBG("Enter hdmi_wp_video_config_interface\n"); | ||||
| 
 | ||||
| 	vsync_pol = timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH; | ||||
| 	hsync_pol = timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH; | ||||
| 
 | ||||
| 	r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); | ||||
| 	r = FLD_MOD(r, vsync_pol, 7, 7); | ||||
| 	r = FLD_MOD(r, hsync_pol, 6, 6); | ||||
| 	r = FLD_MOD(r, timings->interlace, 3, 3); | ||||
| 	r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	u32 timing_h = 0; | ||||
| 	u32 timing_v = 0; | ||||
| 
 | ||||
| 	DSSDBG("Enter hdmi_wp_video_config_timing\n"); | ||||
| 
 | ||||
| 	timing_h |= FLD_VAL(timings->hbp, 31, 20); | ||||
| 	timing_h |= FLD_VAL(timings->hfp, 19, 8); | ||||
| 	timing_h |= FLD_VAL(timings->hsw, 7, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h); | ||||
| 
 | ||||
| 	timing_v |= FLD_VAL(timings->vbp, 31, 20); | ||||
| 	timing_v |= FLD_VAL(timings->vfp, 19, 8); | ||||
| 	timing_v |= FLD_VAL(timings->vsw, 7, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, | ||||
| 		struct omap_video_timings *timings, struct hdmi_config *param) | ||||
| { | ||||
| 	DSSDBG("Enter hdmi_wp_video_init_format\n"); | ||||
| 
 | ||||
| 	video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; | ||||
| 	video_fmt->y_res = param->timings.y_res; | ||||
| 	video_fmt->x_res = param->timings.x_res; | ||||
| 	if (param->timings.interlace) | ||||
| 		video_fmt->y_res /= 2; | ||||
| 
 | ||||
| 	timings->hbp = param->timings.hbp; | ||||
| 	timings->hfp = param->timings.hfp; | ||||
| 	timings->hsw = param->timings.hsw; | ||||
| 	timings->vbp = param->timings.vbp; | ||||
| 	timings->vfp = param->timings.vfp; | ||||
| 	timings->vsw = param->timings.vsw; | ||||
| 	timings->vsync_level = param->timings.vsync_level; | ||||
| 	timings->hsync_level = param->timings.hsync_level; | ||||
| 	timings->interlace = param->timings.interlace; | ||||
| } | ||||
| 
 | ||||
| #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) | ||||
| void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_audio_format *aud_fmt) | ||||
| { | ||||
| 	u32 r; | ||||
| 
 | ||||
| 	DSSDBG("Enter hdmi_wp_audio_config_format\n"); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); | ||||
| 	r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); | ||||
| 	r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); | ||||
| 	r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); | ||||
| 	r = FLD_MOD(r, aud_fmt->type, 4, 4); | ||||
| 	r = FLD_MOD(r, aud_fmt->justification, 3, 3); | ||||
| 	r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); | ||||
| 	r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); | ||||
| 	r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r); | ||||
| } | ||||
| 
 | ||||
| void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, | ||||
| 		struct hdmi_audio_dma *aud_dma) | ||||
| { | ||||
| 	u32 r; | ||||
| 
 | ||||
| 	DSSDBG("Enter hdmi_wp_audio_config_dma\n"); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2); | ||||
| 	r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); | ||||
| 	r = FLD_MOD(r, aud_dma->block_size, 7, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r); | ||||
| 
 | ||||
| 	r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL); | ||||
| 	r = FLD_MOD(r, aud_dma->mode, 9, 9); | ||||
| 	r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); | ||||
| 	hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r); | ||||
| } | ||||
| 
 | ||||
| int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable) | ||||
| { | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) | ||||
| { | ||||
| 	REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) | ||||
| { | ||||
| 	struct resource *res; | ||||
| 
 | ||||
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp"); | ||||
| 	if (!res) { | ||||
| 		DSSERR("can't get WP mem resource\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	wp->base = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(wp->base)) { | ||||
| 		DSSERR("can't ioremap HDMI WP\n"); | ||||
| 		return PTR_ERR(wp->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										531
									
								
								drivers/video/fbdev/omap2/dss/manager-sysfs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										531
									
								
								drivers/video/fbdev/omap2/dss/manager-sysfs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,531 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "MANAGER" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/jiffies.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) | ||||
| { | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) | ||||
| { | ||||
| 	struct omap_dss_device *dssdev = mgr->get_device(mgr); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ? | ||||
| 			dssdev->name : "<none>"); | ||||
| } | ||||
| 
 | ||||
| static int manager_display_match(struct omap_dss_device *dssdev, void *data) | ||||
| { | ||||
| 	const char *str = data; | ||||
| 
 | ||||
| 	return sysfs_streq(dssdev->name, str); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_display_store(struct omap_overlay_manager *mgr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r = 0; | ||||
| 	size_t len = size; | ||||
| 	struct omap_dss_device *dssdev = NULL; | ||||
| 	struct omap_dss_device *old_dssdev; | ||||
| 
 | ||||
| 	if (buf[size-1] == '\n') | ||||
| 		--len; | ||||
| 
 | ||||
| 	if (len > 0) | ||||
| 		dssdev = omap_dss_find_device((void *)buf, | ||||
| 			manager_display_match); | ||||
| 
 | ||||
| 	if (len > 0 && dssdev == NULL) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (dssdev) { | ||||
| 		DSSDBG("display %s found\n", dssdev->name); | ||||
| 
 | ||||
| 		if (omapdss_device_is_connected(dssdev)) { | ||||
| 			DSSERR("new display is already connected\n"); | ||||
| 			r = -EINVAL; | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 
 | ||||
| 		if (omapdss_device_is_enabled(dssdev)) { | ||||
| 			DSSERR("new display is not disabled\n"); | ||||
| 			r = -EINVAL; | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	old_dssdev = mgr->get_device(mgr); | ||||
| 	if (old_dssdev) { | ||||
| 		if (omapdss_device_is_enabled(old_dssdev)) { | ||||
| 			DSSERR("old display is not disabled\n"); | ||||
| 			r = -EINVAL; | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 
 | ||||
| 		old_dssdev->driver->disconnect(old_dssdev); | ||||
| 	} | ||||
| 
 | ||||
| 	if (dssdev) { | ||||
| 		r = dssdev->driver->connect(dssdev); | ||||
| 		if (r) { | ||||
| 			DSSERR("failed to connect new device\n"); | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 
 | ||||
| 		old_dssdev = mgr->get_device(mgr); | ||||
| 		if (old_dssdev != dssdev) { | ||||
| 			DSSERR("failed to connect device to this manager\n"); | ||||
| 			dssdev->driver->disconnect(dssdev); | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 
 | ||||
| 		r = mgr->apply(mgr); | ||||
| 		if (r) { | ||||
| 			DSSERR("failed to apply dispc config\n"); | ||||
| 			goto put_device; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| put_device: | ||||
| 	if (dssdev) | ||||
| 		omap_dss_put_device(dssdev); | ||||
| 
 | ||||
| 	return r ? r : size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, | ||||
| 					  char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, | ||||
| 					   const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	u32 color; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = kstrtouint(buf, 0, &color); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.default_color = color; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static const char *trans_key_type_str[] = { | ||||
| 	"gfx-destination", | ||||
| 	"video-source", | ||||
| }; | ||||
| 
 | ||||
| static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, | ||||
| 					   char *buf) | ||||
| { | ||||
| 	enum omap_dss_trans_key_type key_type; | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	key_type = info.trans_key_type; | ||||
| 	BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, | ||||
| 					    const char *buf, size_t size) | ||||
| { | ||||
| 	enum omap_dss_trans_key_type key_type; | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	int r; | ||||
| 
 | ||||
| 	for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | ||||
| 			key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { | ||||
| 		if (sysfs_streq(buf, trans_key_type_str[key_type])) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (key_type == ARRAY_SIZE(trans_key_type_str)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.trans_key_type = key_type; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, | ||||
| 					    char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, | ||||
| 					     const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	u32 key_value; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = kstrtouint(buf, 0, &key_value); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.trans_key = key_value; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, | ||||
| 					      char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, | ||||
| 					       const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	bool enable; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = strtobool(buf, &enable); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.trans_enabled = enable; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_alpha_blending_enabled_show( | ||||
| 		struct omap_overlay_manager *mgr, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", | ||||
| 		info.partial_alpha_enabled); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_alpha_blending_enabled_store( | ||||
| 		struct omap_overlay_manager *mgr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	bool enable; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = strtobool(buf, &enable); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.partial_alpha_enabled = enable; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, | ||||
| 		char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	int r; | ||||
| 	bool enable; | ||||
| 
 | ||||
| 	if (!dss_has_feature(FEAT_CPR)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = strtobool(buf, &enable); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	if (info.cpr_enable == enable) | ||||
| 		return size; | ||||
| 
 | ||||
| 	info.cpr_enable = enable; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, | ||||
| 		char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, | ||||
| 			"%d %d %d %d %d %d %d %d %d\n", | ||||
| 			info.cpr_coefs.rr, | ||||
| 			info.cpr_coefs.rg, | ||||
| 			info.cpr_coefs.rb, | ||||
| 			info.cpr_coefs.gr, | ||||
| 			info.cpr_coefs.gg, | ||||
| 			info.cpr_coefs.gb, | ||||
| 			info.cpr_coefs.br, | ||||
| 			info.cpr_coefs.bg, | ||||
| 			info.cpr_coefs.bb); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager_info info; | ||||
| 	struct omap_dss_cpr_coefs coefs; | ||||
| 	int r, i; | ||||
| 	s16 *arr; | ||||
| 
 | ||||
| 	if (!dss_has_feature(FEAT_CPR)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd", | ||||
| 				&coefs.rr, &coefs.rg, &coefs.rb, | ||||
| 				&coefs.gr, &coefs.gg, &coefs.gb, | ||||
| 				&coefs.br, &coefs.bg, &coefs.bb) != 9) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb, | ||||
| 		coefs.gr, coefs.gg, coefs.gb, | ||||
| 		coefs.br, coefs.bg, coefs.bb }; | ||||
| 
 | ||||
| 	for (i = 0; i < 9; ++i) { | ||||
| 		if (arr[i] < -512 || arr[i] > 511) | ||||
| 			return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	mgr->get_manager_info(mgr, &info); | ||||
| 
 | ||||
| 	info.cpr_coefs = coefs; | ||||
| 
 | ||||
| 	r = mgr->set_manager_info(mgr, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = mgr->apply(mgr); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| struct manager_attribute { | ||||
| 	struct attribute attr; | ||||
| 	ssize_t (*show)(struct omap_overlay_manager *, char *); | ||||
| 	ssize_t	(*store)(struct omap_overlay_manager *, const char *, size_t); | ||||
| }; | ||||
| 
 | ||||
| #define MANAGER_ATTR(_name, _mode, _show, _store) \ | ||||
| 	struct manager_attribute manager_attr_##_name = \ | ||||
| 	__ATTR(_name, _mode, _show, _store) | ||||
| 
 | ||||
| static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); | ||||
| static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, | ||||
| 		manager_display_show, manager_display_store); | ||||
| static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, | ||||
| 		manager_default_color_show, manager_default_color_store); | ||||
| static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, | ||||
| 		manager_trans_key_type_show, manager_trans_key_type_store); | ||||
| static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, | ||||
| 		manager_trans_key_value_show, manager_trans_key_value_store); | ||||
| static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, | ||||
| 		manager_trans_key_enabled_show, | ||||
| 		manager_trans_key_enabled_store); | ||||
| static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, | ||||
| 		manager_alpha_blending_enabled_show, | ||||
| 		manager_alpha_blending_enabled_store); | ||||
| static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR, | ||||
| 		manager_cpr_enable_show, | ||||
| 		manager_cpr_enable_store); | ||||
| static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR, | ||||
| 		manager_cpr_coef_show, | ||||
| 		manager_cpr_coef_store); | ||||
| 
 | ||||
| 
 | ||||
| static struct attribute *manager_sysfs_attrs[] = { | ||||
| 	&manager_attr_name.attr, | ||||
| 	&manager_attr_display.attr, | ||||
| 	&manager_attr_default_color.attr, | ||||
| 	&manager_attr_trans_key_type.attr, | ||||
| 	&manager_attr_trans_key_value.attr, | ||||
| 	&manager_attr_trans_key_enabled.attr, | ||||
| 	&manager_attr_alpha_blending_enabled.attr, | ||||
| 	&manager_attr_cpr_enable.attr, | ||||
| 	&manager_attr_cpr_coef.attr, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, | ||||
| 		char *buf) | ||||
| { | ||||
| 	struct omap_overlay_manager *manager; | ||||
| 	struct manager_attribute *manager_attr; | ||||
| 
 | ||||
| 	manager = container_of(kobj, struct omap_overlay_manager, kobj); | ||||
| 	manager_attr = container_of(attr, struct manager_attribute, attr); | ||||
| 
 | ||||
| 	if (!manager_attr->show) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return manager_attr->show(manager, buf); | ||||
| } | ||||
| 
 | ||||
| static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay_manager *manager; | ||||
| 	struct manager_attribute *manager_attr; | ||||
| 
 | ||||
| 	manager = container_of(kobj, struct omap_overlay_manager, kobj); | ||||
| 	manager_attr = container_of(attr, struct manager_attribute, attr); | ||||
| 
 | ||||
| 	if (!manager_attr->store) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return manager_attr->store(manager, buf, size); | ||||
| } | ||||
| 
 | ||||
| static const struct sysfs_ops manager_sysfs_ops = { | ||||
| 	.show = manager_attr_show, | ||||
| 	.store = manager_attr_store, | ||||
| }; | ||||
| 
 | ||||
| static struct kobj_type manager_ktype = { | ||||
| 	.sysfs_ops = &manager_sysfs_ops, | ||||
| 	.default_attrs = manager_sysfs_attrs, | ||||
| }; | ||||
| 
 | ||||
| int dss_manager_kobj_init(struct omap_overlay_manager *mgr, | ||||
| 		struct platform_device *pdev) | ||||
| { | ||||
| 	return kobject_init_and_add(&mgr->kobj, &manager_ktype, | ||||
| 			&pdev->dev.kobj, "manager%d", mgr->id); | ||||
| } | ||||
| 
 | ||||
| void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	kobject_del(&mgr->kobj); | ||||
| 	kobject_put(&mgr->kobj); | ||||
| 
 | ||||
| 	memset(&mgr->kobj, 0, sizeof(mgr->kobj)); | ||||
| } | ||||
							
								
								
									
										263
									
								
								drivers/video/fbdev/omap2/dss/manager.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								drivers/video/fbdev/omap2/dss/manager.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,263 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/manager.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "MANAGER" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/jiffies.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static int num_managers; | ||||
| static struct omap_overlay_manager *managers; | ||||
| 
 | ||||
| int dss_init_overlay_managers(void) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	num_managers = dss_feat_get_num_mgrs(); | ||||
| 
 | ||||
| 	managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers, | ||||
| 			GFP_KERNEL); | ||||
| 
 | ||||
| 	BUG_ON(managers == NULL); | ||||
| 
 | ||||
| 	for (i = 0; i < num_managers; ++i) { | ||||
| 		struct omap_overlay_manager *mgr = &managers[i]; | ||||
| 
 | ||||
| 		switch (i) { | ||||
| 		case 0: | ||||
| 			mgr->name = "lcd"; | ||||
| 			mgr->id = OMAP_DSS_CHANNEL_LCD; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			mgr->name = "tv"; | ||||
| 			mgr->id = OMAP_DSS_CHANNEL_DIGIT; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			mgr->name = "lcd2"; | ||||
| 			mgr->id = OMAP_DSS_CHANNEL_LCD2; | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			mgr->name = "lcd3"; | ||||
| 			mgr->id = OMAP_DSS_CHANNEL_LCD3; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		mgr->caps = 0; | ||||
| 		mgr->supported_displays = | ||||
| 			dss_feat_get_supported_displays(mgr->id); | ||||
| 		mgr->supported_outputs = | ||||
| 			dss_feat_get_supported_outputs(mgr->id); | ||||
| 
 | ||||
| 		INIT_LIST_HEAD(&mgr->overlays); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_init_overlay_managers_sysfs(struct platform_device *pdev) | ||||
| { | ||||
| 	int i, r; | ||||
| 
 | ||||
| 	for (i = 0; i < num_managers; ++i) { | ||||
| 		struct omap_overlay_manager *mgr = &managers[i]; | ||||
| 
 | ||||
| 		r = dss_manager_kobj_init(mgr, pdev); | ||||
| 		if (r) | ||||
| 			DSSERR("failed to create sysfs file\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void dss_uninit_overlay_managers(void) | ||||
| { | ||||
| 	kfree(managers); | ||||
| 	managers = NULL; | ||||
| 	num_managers = 0; | ||||
| } | ||||
| 
 | ||||
| void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < num_managers; ++i) { | ||||
| 		struct omap_overlay_manager *mgr = &managers[i]; | ||||
| 
 | ||||
| 		dss_manager_kobj_uninit(mgr); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int omap_dss_get_num_overlay_managers(void) | ||||
| { | ||||
| 	return num_managers; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); | ||||
| 
 | ||||
| struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) | ||||
| { | ||||
| 	if (num >= num_managers) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return &managers[num]; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_overlay_manager); | ||||
| 
 | ||||
| int dss_mgr_simple_check(struct omap_overlay_manager *mgr, | ||||
| 		const struct omap_overlay_manager_info *info) | ||||
| { | ||||
| 	if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) { | ||||
| 		/*
 | ||||
| 		 * OMAP3 supports only graphics source transparency color key | ||||
| 		 * and alpha blending simultaneously. See TRM 15.4.2.4.2.2 | ||||
| 		 * Alpha Mode. | ||||
| 		 */ | ||||
| 		if (info->partial_alpha_enabled && info->trans_enabled | ||||
| 			&& info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) { | ||||
| 			DSSERR("check_manager: illegal transparency key\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, | ||||
| 		struct omap_overlay_info **overlay_infos) | ||||
| { | ||||
| 	struct omap_overlay *ovl1, *ovl2; | ||||
| 	struct omap_overlay_info *info1, *info2; | ||||
| 
 | ||||
| 	list_for_each_entry(ovl1, &mgr->overlays, list) { | ||||
| 		info1 = overlay_infos[ovl1->id]; | ||||
| 
 | ||||
| 		if (info1 == NULL) | ||||
| 			continue; | ||||
| 
 | ||||
| 		list_for_each_entry(ovl2, &mgr->overlays, list) { | ||||
| 			if (ovl1 == ovl2) | ||||
| 				continue; | ||||
| 
 | ||||
| 			info2 = overlay_infos[ovl2->id]; | ||||
| 
 | ||||
| 			if (info2 == NULL) | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (info1->zorder == info2->zorder) { | ||||
| 				DSSERR("overlays %d and %d have the same " | ||||
| 						"zorder %d\n", | ||||
| 					ovl1->id, ovl2->id, info1->zorder); | ||||
| 				return -EINVAL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_mgr_check_timings(struct omap_overlay_manager *mgr, | ||||
| 		const struct omap_video_timings *timings) | ||||
| { | ||||
| 	if (!dispc_mgr_timings_ok(mgr->id, timings)) { | ||||
| 		DSSERR("check_manager: invalid timings\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr, | ||||
| 		const struct dss_lcd_mgr_config *config) | ||||
| { | ||||
| 	struct dispc_clock_info cinfo = config->clock_info; | ||||
| 	int dl = config->video_port_width; | ||||
| 	bool stallmode = config->stallmode; | ||||
| 	bool fifohandcheck = config->fifohandcheck; | ||||
| 
 | ||||
| 	if (cinfo.lck_div < 1 || cinfo.lck_div > 255) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (cinfo.pck_div < 1 || cinfo.pck_div > 255) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (dl != 12 && dl != 16 && dl != 18 && dl != 24) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* fifohandcheck should be used only with stallmode */ | ||||
| 	if (stallmode == false && fifohandcheck == true) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * io pad mode can be only checked by using dssdev connected to the | ||||
| 	 * manager. Ignore checking these for now, add checks when manager | ||||
| 	 * is capable of holding information related to the connected interface | ||||
| 	 */ | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_mgr_check(struct omap_overlay_manager *mgr, | ||||
| 		struct omap_overlay_manager_info *info, | ||||
| 		const struct omap_video_timings *mgr_timings, | ||||
| 		const struct dss_lcd_mgr_config *lcd_config, | ||||
| 		struct omap_overlay_info **overlay_infos) | ||||
| { | ||||
| 	struct omap_overlay *ovl; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) { | ||||
| 		r = dss_mgr_check_zorder(mgr, overlay_infos); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	r = dss_mgr_check_timings(mgr, mgr_timings); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = dss_mgr_check_lcd_config(mgr, lcd_config); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	list_for_each_entry(ovl, &mgr->overlays, list) { | ||||
| 		struct omap_overlay_info *oi; | ||||
| 		int r; | ||||
| 
 | ||||
| 		oi = overlay_infos[ovl->id]; | ||||
| 
 | ||||
| 		if (oi == NULL) | ||||
| 			continue; | ||||
| 
 | ||||
| 		r = dss_ovl_check(ovl, oi, mgr_timings); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										231
									
								
								drivers/video/fbdev/omap2/dss/omapdss-boot-init.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								drivers/video/fbdev/omap2/dss/omapdss-boot-init.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,231 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2014 Texas Instruments | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| /*
 | ||||
|  * As omapdss panel drivers are omapdss specific, but we want to define the | ||||
|  * DT-data in generic manner, we convert the compatible strings of the panel and | ||||
|  * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have | ||||
|  * both correct DT data and omapdss specific drivers. | ||||
|  * | ||||
|  * When we get generic panel drivers to the kernel, this file will be removed. | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_graph.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/list.h> | ||||
| 
 | ||||
| static struct list_head dss_conv_list __initdata; | ||||
| 
 | ||||
| static const char prefix[] __initconst = "omapdss,"; | ||||
| 
 | ||||
| struct dss_conv_node { | ||||
| 	struct list_head list; | ||||
| 	struct device_node *node; | ||||
| 	bool root; | ||||
| }; | ||||
| 
 | ||||
| static int __init omapdss_count_strings(const struct property *prop) | ||||
| { | ||||
| 	const char *p = prop->value; | ||||
| 	int l = 0, total = 0; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; total < prop->length; total += l, p += l, i++) | ||||
| 		l = strlen(p) + 1; | ||||
| 
 | ||||
| 	return i; | ||||
| } | ||||
| 
 | ||||
| static void __init omapdss_update_prop(struct device_node *node, char *compat, | ||||
| 	int len) | ||||
| { | ||||
| 	struct property *prop; | ||||
| 
 | ||||
| 	prop = kzalloc(sizeof(*prop), GFP_KERNEL); | ||||
| 	if (!prop) | ||||
| 		return; | ||||
| 
 | ||||
| 	prop->name = "compatible"; | ||||
| 	prop->value = compat; | ||||
| 	prop->length = len; | ||||
| 
 | ||||
| 	of_update_property(node, prop); | ||||
| } | ||||
| 
 | ||||
| static void __init omapdss_prefix_strcpy(char *dst, int dst_len, | ||||
| 	const char *src, int src_len) | ||||
| { | ||||
| 	size_t total = 0; | ||||
| 
 | ||||
| 	while (total < src_len) { | ||||
| 		size_t l = strlen(src) + 1; | ||||
| 
 | ||||
| 		strcpy(dst, prefix); | ||||
| 		dst += strlen(prefix); | ||||
| 
 | ||||
| 		strcpy(dst, src); | ||||
| 		dst += l; | ||||
| 
 | ||||
| 		src += l; | ||||
| 		total += l; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* prepend compatible property strings with "omapdss," */ | ||||
| static void __init omapdss_omapify_node(struct device_node *node) | ||||
| { | ||||
| 	struct property *prop; | ||||
| 	char *new_compat; | ||||
| 	int num_strs; | ||||
| 	int new_len; | ||||
| 
 | ||||
| 	prop = of_find_property(node, "compatible", NULL); | ||||
| 
 | ||||
| 	if (!prop || !prop->value) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (strnlen(prop->value, prop->length) >= prop->length) | ||||
| 		return; | ||||
| 
 | ||||
| 	/* is it already prefixed? */ | ||||
| 	if (strncmp(prefix, prop->value, strlen(prefix)) == 0) | ||||
| 		return; | ||||
| 
 | ||||
| 	num_strs = omapdss_count_strings(prop); | ||||
| 
 | ||||
| 	new_len = prop->length + strlen(prefix) * num_strs; | ||||
| 	new_compat = kmalloc(new_len, GFP_KERNEL); | ||||
| 
 | ||||
| 	omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length); | ||||
| 
 | ||||
| 	omapdss_update_prop(node, new_compat, new_len); | ||||
| } | ||||
| 
 | ||||
| static void __init omapdss_add_to_list(struct device_node *node, bool root) | ||||
| { | ||||
| 	struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node), | ||||
| 		GFP_KERNEL); | ||||
| 	if (n) { | ||||
| 		n->node = node; | ||||
| 		n->root = root; | ||||
| 		list_add(&n->list, &dss_conv_list); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static bool __init omapdss_list_contains(const struct device_node *node) | ||||
| { | ||||
| 	struct dss_conv_node *n; | ||||
| 
 | ||||
| 	list_for_each_entry(n, &dss_conv_list, list) { | ||||
| 		if (n->node == node) | ||||
| 			return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static void __init omapdss_walk_device(struct device_node *node, bool root) | ||||
| { | ||||
| 	struct device_node *n; | ||||
| 
 | ||||
| 	omapdss_add_to_list(node, root); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * of_graph_get_remote_port_parent() prints an error if there is no | ||||
| 	 * port/ports node. To avoid that, check first that there's the node. | ||||
| 	 */ | ||||
| 	n = of_get_child_by_name(node, "ports"); | ||||
| 	if (!n) | ||||
| 		n = of_get_child_by_name(node, "port"); | ||||
| 	if (!n) | ||||
| 		return; | ||||
| 
 | ||||
| 	of_node_put(n); | ||||
| 
 | ||||
| 	n = NULL; | ||||
| 	while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { | ||||
| 		struct device_node *pn; | ||||
| 
 | ||||
| 		pn = of_graph_get_remote_port_parent(n); | ||||
| 
 | ||||
| 		if (!pn) { | ||||
| 			of_node_put(n); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { | ||||
| 			of_node_put(pn); | ||||
| 			of_node_put(n); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		omapdss_walk_device(pn, false); | ||||
| 
 | ||||
| 		of_node_put(n); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id omapdss_of_match[] __initconst = { | ||||
| 	{ .compatible = "ti,omap2-dss", }, | ||||
| 	{ .compatible = "ti,omap3-dss", }, | ||||
| 	{ .compatible = "ti,omap4-dss", }, | ||||
| 	{ .compatible = "ti,omap5-dss", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static int __init omapdss_boot_init(void) | ||||
| { | ||||
| 	struct device_node *dss, *child; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&dss_conv_list); | ||||
| 
 | ||||
| 	dss = of_find_matching_node(NULL, omapdss_of_match); | ||||
| 
 | ||||
| 	if (dss == NULL || !of_device_is_available(dss)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	omapdss_walk_device(dss, true); | ||||
| 
 | ||||
| 	for_each_available_child_of_node(dss, child) { | ||||
| 		if (!of_find_property(child, "compatible", NULL)) { | ||||
| 			of_node_put(child); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		omapdss_walk_device(child, true); | ||||
| 	} | ||||
| 
 | ||||
| 	while (!list_empty(&dss_conv_list)) { | ||||
| 		struct dss_conv_node *n; | ||||
| 
 | ||||
| 		n = list_first_entry(&dss_conv_list, struct dss_conv_node, | ||||
| 			list); | ||||
| 
 | ||||
| 		if (!n->root) | ||||
| 			omapdss_omapify_node(n->node); | ||||
| 
 | ||||
| 		list_del(&n->list); | ||||
| 		of_node_put(n->node); | ||||
| 		kfree(n); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(omapdss_boot_init); | ||||
							
								
								
									
										254
									
								
								drivers/video/fbdev/omap2/dss/output.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								drivers/video/fbdev/omap2/dss/output.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,254 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2012 Texas Instruments Ltd | ||||
|  * Author: Archit Taneja <archit@ti.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| 
 | ||||
| static LIST_HEAD(output_list); | ||||
| static DEFINE_MUTEX(output_lock); | ||||
| 
 | ||||
| int omapdss_output_set_device(struct omap_dss_device *out, | ||||
| 		struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&output_lock); | ||||
| 
 | ||||
| 	if (out->dst) { | ||||
| 		DSSERR("output already has device %s connected to it\n", | ||||
| 			out->dst->name); | ||||
| 		r = -EINVAL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (out->output_type != dssdev->type) { | ||||
| 		DSSERR("output type and display type don't match\n"); | ||||
| 		r = -EINVAL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	out->dst = dssdev; | ||||
| 	dssdev->src = out; | ||||
| 
 | ||||
| 	mutex_unlock(&output_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	mutex_unlock(&output_lock); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_output_set_device); | ||||
| 
 | ||||
| int omapdss_output_unset_device(struct omap_dss_device *out) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	mutex_lock(&output_lock); | ||||
| 
 | ||||
| 	if (!out->dst) { | ||||
| 		DSSERR("output doesn't have a device connected to it\n"); | ||||
| 		r = -EINVAL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (out->dst->state != OMAP_DSS_DISPLAY_DISABLED) { | ||||
| 		DSSERR("device %s is not disabled, cannot unset device\n", | ||||
| 				out->dst->name); | ||||
| 		r = -EINVAL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	out->dst->src = NULL; | ||||
| 	out->dst = NULL; | ||||
| 
 | ||||
| 	mutex_unlock(&output_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	mutex_unlock(&output_lock); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_output_unset_device); | ||||
| 
 | ||||
| int omapdss_register_output(struct omap_dss_device *out) | ||||
| { | ||||
| 	list_add_tail(&out->list, &output_list); | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_register_output); | ||||
| 
 | ||||
| void omapdss_unregister_output(struct omap_dss_device *out) | ||||
| { | ||||
| 	list_del(&out->list); | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_unregister_output); | ||||
| 
 | ||||
| struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) | ||||
| { | ||||
| 	struct omap_dss_device *out; | ||||
| 
 | ||||
| 	list_for_each_entry(out, &output_list, list) { | ||||
| 		if (out->id == id) | ||||
| 			return out; | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_output); | ||||
| 
 | ||||
| struct omap_dss_device *omap_dss_find_output(const char *name) | ||||
| { | ||||
| 	struct omap_dss_device *out; | ||||
| 
 | ||||
| 	list_for_each_entry(out, &output_list, list) { | ||||
| 		if (strcmp(out->name, name) == 0) | ||||
| 			return omap_dss_get_device(out); | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_find_output); | ||||
| 
 | ||||
| struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node) | ||||
| { | ||||
| 	struct omap_dss_device *out; | ||||
| 
 | ||||
| 	list_for_each_entry(out, &output_list, list) { | ||||
| 		if (out->dev->of_node == node) | ||||
| 			return omap_dss_get_device(out); | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_find_output_by_node); | ||||
| 
 | ||||
| struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	while (dssdev->src) | ||||
| 		dssdev = dssdev->src; | ||||
| 
 | ||||
| 	if (dssdev->id != 0) | ||||
| 		return omap_dss_get_device(dssdev); | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_find_output_from_display); | ||||
| 
 | ||||
| struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out; | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 
 | ||||
| 	out = omapdss_find_output_from_display(dssdev); | ||||
| 
 | ||||
| 	if (out == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	mgr = out->manager; | ||||
| 
 | ||||
| 	omap_dss_put_device(out); | ||||
| 
 | ||||
| 	return mgr; | ||||
| } | ||||
| EXPORT_SYMBOL(omapdss_find_mgr_from_display); | ||||
| 
 | ||||
| static const struct dss_mgr_ops *dss_mgr_ops; | ||||
| 
 | ||||
| int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops) | ||||
| { | ||||
| 	if (dss_mgr_ops) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	dss_mgr_ops = mgr_ops; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL(dss_install_mgr_ops); | ||||
| 
 | ||||
| void dss_uninstall_mgr_ops(void) | ||||
| { | ||||
| 	dss_mgr_ops = NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(dss_uninstall_mgr_ops); | ||||
| 
 | ||||
| int dss_mgr_connect(struct omap_overlay_manager *mgr, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	return dss_mgr_ops->connect(mgr, dst); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_connect); | ||||
| 
 | ||||
| void dss_mgr_disconnect(struct omap_overlay_manager *mgr, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	dss_mgr_ops->disconnect(mgr, dst); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_disconnect); | ||||
| 
 | ||||
| void dss_mgr_set_timings(struct omap_overlay_manager *mgr, | ||||
| 		const struct omap_video_timings *timings) | ||||
| { | ||||
| 	dss_mgr_ops->set_timings(mgr, timings); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_set_timings); | ||||
| 
 | ||||
| void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr, | ||||
| 		const struct dss_lcd_mgr_config *config) | ||||
| { | ||||
| 	dss_mgr_ops->set_lcd_config(mgr, config); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_set_lcd_config); | ||||
| 
 | ||||
| int dss_mgr_enable(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	return dss_mgr_ops->enable(mgr); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_enable); | ||||
| 
 | ||||
| void dss_mgr_disable(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	dss_mgr_ops->disable(mgr); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_disable); | ||||
| 
 | ||||
| void dss_mgr_start_update(struct omap_overlay_manager *mgr) | ||||
| { | ||||
| 	dss_mgr_ops->start_update(mgr); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_start_update); | ||||
| 
 | ||||
| int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr, | ||||
| 		void (*handler)(void *), void *data) | ||||
| { | ||||
| 	return dss_mgr_ops->register_framedone_handler(mgr, handler, data); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_register_framedone_handler); | ||||
| 
 | ||||
| void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr, | ||||
| 		void (*handler)(void *), void *data) | ||||
| { | ||||
| 	dss_mgr_ops->unregister_framedone_handler(mgr, handler, data); | ||||
| } | ||||
| EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler); | ||||
							
								
								
									
										456
									
								
								drivers/video/fbdev/omap2/dss/overlay-sysfs.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										456
									
								
								drivers/video/fbdev/omap2/dss/overlay-sysfs.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,456 @@ | |||
| /*
 | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "OVERLAY" | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include <linux/kobject.h> | ||||
| #include <linux/platform_device.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	return snprintf(buf, PAGE_SIZE, "%s\n", | ||||
| 			ovl->manager ? ovl->manager->name : "<none>"); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, | ||||
| 		size_t size) | ||||
| { | ||||
| 	int i, r; | ||||
| 	struct omap_overlay_manager *mgr = NULL; | ||||
| 	struct omap_overlay_manager *old_mgr; | ||||
| 	int len = size; | ||||
| 
 | ||||
| 	if (buf[size-1] == '\n') | ||||
| 		--len; | ||||
| 
 | ||||
| 	if (len > 0) { | ||||
| 		for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { | ||||
| 			mgr = omap_dss_get_overlay_manager(i); | ||||
| 
 | ||||
| 			if (sysfs_streq(buf, mgr->name)) | ||||
| 				break; | ||||
| 
 | ||||
| 			mgr = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (len > 0 && mgr == NULL) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (mgr) | ||||
| 		DSSDBG("manager %s found\n", mgr->name); | ||||
| 
 | ||||
| 	if (mgr == ovl->manager) | ||||
| 		return size; | ||||
| 
 | ||||
| 	old_mgr = ovl->manager; | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	/* detach old manager */ | ||||
| 	if (old_mgr) { | ||||
| 		r = ovl->unset_manager(ovl); | ||||
| 		if (r) { | ||||
| 			DSSERR("detach failed\n"); | ||||
| 			goto err; | ||||
| 		} | ||||
| 
 | ||||
| 		r = old_mgr->apply(old_mgr); | ||||
| 		if (r) | ||||
| 			goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (mgr) { | ||||
| 		r = ovl->set_manager(ovl, mgr); | ||||
| 		if (r) { | ||||
| 			DSSERR("Failed to attach overlay\n"); | ||||
| 			goto err; | ||||
| 		} | ||||
| 
 | ||||
| 		r = mgr->apply(mgr); | ||||
| 		if (r) | ||||
| 			goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	return size; | ||||
| 
 | ||||
| err: | ||||
| 	dispc_runtime_put(); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d,%d\n", | ||||
| 			info.width, info.height); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", info.screen_width); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d,%d\n", | ||||
| 			info.pos_x, info.pos_y); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_position_store(struct omap_overlay *ovl, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	char *last; | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	info.pos_x = simple_strtoul(buf, &last, 10); | ||||
| 	++last; | ||||
| 	if (last - buf >= size) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	info.pos_y = simple_strtoul(last, &last, 10); | ||||
| 
 | ||||
| 	r = ovl->set_overlay_info(ovl, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (ovl->manager) { | ||||
| 		r = ovl->manager->apply(ovl->manager); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d,%d\n", | ||||
| 			info.out_width, info.out_height); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_output_size_store(struct omap_overlay *ovl, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	char *last; | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	info.out_width = simple_strtoul(buf, &last, 10); | ||||
| 	++last; | ||||
| 	if (last - buf >= size) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	info.out_height = simple_strtoul(last, &last, 10); | ||||
| 
 | ||||
| 	r = ovl->set_overlay_info(ovl, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (ovl->manager) { | ||||
| 		r = ovl->manager->apply(ovl->manager); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl)); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, | ||||
| 		size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	bool enable; | ||||
| 
 | ||||
| 	r = strtobool(buf, &enable); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (enable) | ||||
| 		r = ovl->enable(ovl); | ||||
| 	else | ||||
| 		r = ovl->disable(ovl); | ||||
| 
 | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", | ||||
| 			info.global_alpha); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	u8 alpha; | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = kstrtou8(buf, 0, &alpha); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	info.global_alpha = alpha; | ||||
| 
 | ||||
| 	r = ovl->set_overlay_info(ovl, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (ovl->manager) { | ||||
| 		r = ovl->manager->apply(ovl->manager); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl, | ||||
| 		char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", | ||||
| 			info.pre_mult_alpha); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	u8 alpha; | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = kstrtou8(buf, 0, &alpha); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	info.pre_mult_alpha = alpha; | ||||
| 
 | ||||
| 	r = ovl->set_overlay_info(ovl, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (ovl->manager) { | ||||
| 		r = ovl->manager->apply(ovl->manager); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) | ||||
| { | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	return snprintf(buf, PAGE_SIZE, "%d\n", info.zorder); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_zorder_store(struct omap_overlay *ovl, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	int r; | ||||
| 	u8 zorder; | ||||
| 	struct omap_overlay_info info; | ||||
| 
 | ||||
| 	if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = kstrtou8(buf, 0, &zorder); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	ovl->get_overlay_info(ovl, &info); | ||||
| 
 | ||||
| 	info.zorder = zorder; | ||||
| 
 | ||||
| 	r = ovl->set_overlay_info(ovl, &info); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (ovl->manager) { | ||||
| 		r = ovl->manager->apply(ovl->manager); | ||||
| 		if (r) | ||||
| 			return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| struct overlay_attribute { | ||||
| 	struct attribute attr; | ||||
| 	ssize_t (*show)(struct omap_overlay *, char *); | ||||
| 	ssize_t	(*store)(struct omap_overlay *, const char *, size_t); | ||||
| }; | ||||
| 
 | ||||
| #define OVERLAY_ATTR(_name, _mode, _show, _store) \ | ||||
| 	struct overlay_attribute overlay_attr_##_name = \ | ||||
| 	__ATTR(_name, _mode, _show, _store) | ||||
| 
 | ||||
| static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); | ||||
| static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, | ||||
| 		overlay_manager_show, overlay_manager_store); | ||||
| static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); | ||||
| static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); | ||||
| static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, | ||||
| 		overlay_position_show, overlay_position_store); | ||||
| static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, | ||||
| 		overlay_output_size_show, overlay_output_size_store); | ||||
| static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, | ||||
| 		overlay_enabled_show, overlay_enabled_store); | ||||
| static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, | ||||
| 		overlay_global_alpha_show, overlay_global_alpha_store); | ||||
| static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR, | ||||
| 		overlay_pre_mult_alpha_show, | ||||
| 		overlay_pre_mult_alpha_store); | ||||
| static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR, | ||||
| 		overlay_zorder_show, overlay_zorder_store); | ||||
| 
 | ||||
| static struct attribute *overlay_sysfs_attrs[] = { | ||||
| 	&overlay_attr_name.attr, | ||||
| 	&overlay_attr_manager.attr, | ||||
| 	&overlay_attr_input_size.attr, | ||||
| 	&overlay_attr_screen_width.attr, | ||||
| 	&overlay_attr_position.attr, | ||||
| 	&overlay_attr_output_size.attr, | ||||
| 	&overlay_attr_enabled.attr, | ||||
| 	&overlay_attr_global_alpha.attr, | ||||
| 	&overlay_attr_pre_mult_alpha.attr, | ||||
| 	&overlay_attr_zorder.attr, | ||||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, | ||||
| 		char *buf) | ||||
| { | ||||
| 	struct omap_overlay *overlay; | ||||
| 	struct overlay_attribute *overlay_attr; | ||||
| 
 | ||||
| 	overlay = container_of(kobj, struct omap_overlay, kobj); | ||||
| 	overlay_attr = container_of(attr, struct overlay_attribute, attr); | ||||
| 
 | ||||
| 	if (!overlay_attr->show) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return overlay_attr->show(overlay, buf); | ||||
| } | ||||
| 
 | ||||
| static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, | ||||
| 		const char *buf, size_t size) | ||||
| { | ||||
| 	struct omap_overlay *overlay; | ||||
| 	struct overlay_attribute *overlay_attr; | ||||
| 
 | ||||
| 	overlay = container_of(kobj, struct omap_overlay, kobj); | ||||
| 	overlay_attr = container_of(attr, struct overlay_attribute, attr); | ||||
| 
 | ||||
| 	if (!overlay_attr->store) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	return overlay_attr->store(overlay, buf, size); | ||||
| } | ||||
| 
 | ||||
| static const struct sysfs_ops overlay_sysfs_ops = { | ||||
| 	.show = overlay_attr_show, | ||||
| 	.store = overlay_attr_store, | ||||
| }; | ||||
| 
 | ||||
| static struct kobj_type overlay_ktype = { | ||||
| 	.sysfs_ops = &overlay_sysfs_ops, | ||||
| 	.default_attrs = overlay_sysfs_attrs, | ||||
| }; | ||||
| 
 | ||||
| int dss_overlay_kobj_init(struct omap_overlay *ovl, | ||||
| 		struct platform_device *pdev) | ||||
| { | ||||
| 	return kobject_init_and_add(&ovl->kobj, &overlay_ktype, | ||||
| 			&pdev->dev.kobj, "overlay%d", ovl->id); | ||||
| } | ||||
| 
 | ||||
| void dss_overlay_kobj_uninit(struct omap_overlay *ovl) | ||||
| { | ||||
| 	kobject_del(&ovl->kobj); | ||||
| 	kobject_put(&ovl->kobj); | ||||
| } | ||||
							
								
								
									
										202
									
								
								drivers/video/fbdev/omap2/dss/overlay.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								drivers/video/fbdev/omap2/dss/overlay.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,202 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/overlay.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * Some code and ideas taken from drivers/video/omap/ driver | ||||
|  * by Imre Deak. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "OVERLAY" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/sysfs.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| static int num_overlays; | ||||
| static struct omap_overlay *overlays; | ||||
| 
 | ||||
| int omap_dss_get_num_overlays(void) | ||||
| { | ||||
| 	return num_overlays; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_num_overlays); | ||||
| 
 | ||||
| struct omap_overlay *omap_dss_get_overlay(int num) | ||||
| { | ||||
| 	if (num >= num_overlays) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return &overlays[num]; | ||||
| } | ||||
| EXPORT_SYMBOL(omap_dss_get_overlay); | ||||
| 
 | ||||
| void dss_init_overlays(struct platform_device *pdev) | ||||
| { | ||||
| 	int i, r; | ||||
| 
 | ||||
| 	num_overlays = dss_feat_get_num_ovls(); | ||||
| 
 | ||||
| 	overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays, | ||||
| 			GFP_KERNEL); | ||||
| 
 | ||||
| 	BUG_ON(overlays == NULL); | ||||
| 
 | ||||
| 	for (i = 0; i < num_overlays; ++i) { | ||||
| 		struct omap_overlay *ovl = &overlays[i]; | ||||
| 
 | ||||
| 		switch (i) { | ||||
| 		case 0: | ||||
| 			ovl->name = "gfx"; | ||||
| 			ovl->id = OMAP_DSS_GFX; | ||||
| 			break; | ||||
| 		case 1: | ||||
| 			ovl->name = "vid1"; | ||||
| 			ovl->id = OMAP_DSS_VIDEO1; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			ovl->name = "vid2"; | ||||
| 			ovl->id = OMAP_DSS_VIDEO2; | ||||
| 			break; | ||||
| 		case 3: | ||||
| 			ovl->name = "vid3"; | ||||
| 			ovl->id = OMAP_DSS_VIDEO3; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		ovl->caps = dss_feat_get_overlay_caps(ovl->id); | ||||
| 		ovl->supported_modes = | ||||
| 			dss_feat_get_supported_color_modes(ovl->id); | ||||
| 
 | ||||
| 		r = dss_overlay_kobj_init(ovl, pdev); | ||||
| 		if (r) | ||||
| 			DSSERR("failed to create sysfs file\n"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void dss_uninit_overlays(struct platform_device *pdev) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < num_overlays; ++i) { | ||||
| 		struct omap_overlay *ovl = &overlays[i]; | ||||
| 		dss_overlay_kobj_uninit(ovl); | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(overlays); | ||||
| 	overlays = NULL; | ||||
| 	num_overlays = 0; | ||||
| } | ||||
| 
 | ||||
| int dss_ovl_simple_check(struct omap_overlay *ovl, | ||||
| 		const struct omap_overlay_info *info) | ||||
| { | ||||
| 	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { | ||||
| 		if (info->out_width != 0 && info->width != info->out_width) { | ||||
| 			DSSERR("check_overlay: overlay %d doesn't support " | ||||
| 					"scaling\n", ovl->id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		if (info->out_height != 0 && info->height != info->out_height) { | ||||
| 			DSSERR("check_overlay: overlay %d doesn't support " | ||||
| 					"scaling\n", ovl->id); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ((ovl->supported_modes & info->color_mode) == 0) { | ||||
| 		DSSERR("check_overlay: overlay %d doesn't support mode %d\n", | ||||
| 				ovl->id, info->color_mode); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (info->zorder >= omap_dss_get_num_overlays()) { | ||||
| 		DSSERR("check_overlay: zorder %d too high\n", info->zorder); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dss_feat_rotation_type_supported(info->rotation_type) == 0) { | ||||
| 		DSSERR("check_overlay: rotation type %d not supported\n", | ||||
| 				info->rotation_type); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, | ||||
| 		const struct omap_video_timings *mgr_timings) | ||||
| { | ||||
| 	u16 outw, outh; | ||||
| 	u16 dw, dh; | ||||
| 
 | ||||
| 	dw = mgr_timings->x_res; | ||||
| 	dh = mgr_timings->y_res; | ||||
| 
 | ||||
| 	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { | ||||
| 		outw = info->width; | ||||
| 		outh = info->height; | ||||
| 	} else { | ||||
| 		if (info->out_width == 0) | ||||
| 			outw = info->width; | ||||
| 		else | ||||
| 			outw = info->out_width; | ||||
| 
 | ||||
| 		if (info->out_height == 0) | ||||
| 			outh = info->height; | ||||
| 		else | ||||
| 			outh = info->out_height; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dw < info->pos_x + outw) { | ||||
| 		DSSERR("overlay %d horizontally not inside the display area " | ||||
| 				"(%d + %d >= %d)\n", | ||||
| 				ovl->id, info->pos_x, outw, dw); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (dh < info->pos_y + outh) { | ||||
| 		DSSERR("overlay %d vertically not inside the display area " | ||||
| 				"(%d + %d >= %d)\n", | ||||
| 				ovl->id, info->pos_y, outh, dh); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Checks if replication logic should be used. Only use when overlay is in | ||||
|  * RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp | ||||
|  */ | ||||
| bool dss_ovl_use_replication(struct dss_lcd_mgr_config config, | ||||
| 		enum omap_color_mode mode) | ||||
| { | ||||
| 	if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) | ||||
| 		return false; | ||||
| 
 | ||||
| 	return config.video_port_width > 16; | ||||
| } | ||||
							
								
								
									
										1059
									
								
								drivers/video/fbdev/omap2/dss/rfbi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1059
									
								
								drivers/video/fbdev/omap2/dss/rfbi.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										434
									
								
								drivers/video/fbdev/omap2/dss/sdi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								drivers/video/fbdev/omap2/dss/sdi.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,434 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/sdi.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "SDI" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| #include "dss.h" | ||||
| 
 | ||||
| static struct { | ||||
| 	struct platform_device *pdev; | ||||
| 
 | ||||
| 	bool update_enabled; | ||||
| 	struct regulator *vdds_sdi_reg; | ||||
| 
 | ||||
| 	struct dss_lcd_mgr_config mgr_config; | ||||
| 	struct omap_video_timings timings; | ||||
| 	int datapairs; | ||||
| 
 | ||||
| 	struct omap_dss_device output; | ||||
| 
 | ||||
| 	bool port_initialized; | ||||
| } sdi; | ||||
| 
 | ||||
| struct sdi_clk_calc_ctx { | ||||
| 	unsigned long pck_min, pck_max; | ||||
| 
 | ||||
| 	unsigned long fck; | ||||
| 	struct dispc_clock_info dispc_cinfo; | ||||
| }; | ||||
| 
 | ||||
| static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, | ||||
| 		unsigned long pck, void *data) | ||||
| { | ||||
| 	struct sdi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	ctx->dispc_cinfo.lck_div = lckd; | ||||
| 	ctx->dispc_cinfo.pck_div = pckd; | ||||
| 	ctx->dispc_cinfo.lck = lck; | ||||
| 	ctx->dispc_cinfo.pck = pck; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static bool dpi_calc_dss_cb(unsigned long fck, void *data) | ||||
| { | ||||
| 	struct sdi_clk_calc_ctx *ctx = data; | ||||
| 
 | ||||
| 	ctx->fck = fck; | ||||
| 
 | ||||
| 	return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max, | ||||
| 			dpi_calc_dispc_cb, ctx); | ||||
| } | ||||
| 
 | ||||
| static int sdi_calc_clock_div(unsigned long pclk, | ||||
| 		unsigned long *fck, | ||||
| 		struct dispc_clock_info *dispc_cinfo) | ||||
| { | ||||
| 	int i; | ||||
| 	struct sdi_clk_calc_ctx ctx; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * DSS fclk gives us very few possibilities, so finding a good pixel | ||||
| 	 * clock may not be possible. We try multiple times to find the clock, | ||||
| 	 * each time widening the pixel clock range we look for, up to | ||||
| 	 * +/- 1MHz. | ||||
| 	 */ | ||||
| 
 | ||||
| 	for (i = 0; i < 10; ++i) { | ||||
| 		bool ok; | ||||
| 
 | ||||
| 		memset(&ctx, 0, sizeof(ctx)); | ||||
| 		if (pclk > 1000 * i * i * i) | ||||
| 			ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu); | ||||
| 		else | ||||
| 			ctx.pck_min = 0; | ||||
| 		ctx.pck_max = pclk + 1000 * i * i * i; | ||||
| 
 | ||||
| 		ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx); | ||||
| 		if (ok) { | ||||
| 			*fck = ctx.fck; | ||||
| 			*dispc_cinfo = ctx.dispc_cinfo; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = sdi.output.manager; | ||||
| 
 | ||||
| 	sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; | ||||
| 
 | ||||
| 	sdi.mgr_config.stallmode = false; | ||||
| 	sdi.mgr_config.fifohandcheck = false; | ||||
| 
 | ||||
| 	sdi.mgr_config.video_port_width = 24; | ||||
| 	sdi.mgr_config.lcden_sig_polarity = 1; | ||||
| 
 | ||||
| 	dss_mgr_set_lcd_config(mgr, &sdi.mgr_config); | ||||
| } | ||||
| 
 | ||||
| static int sdi_display_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &sdi.output; | ||||
| 	struct omap_video_timings *t = &sdi.timings; | ||||
| 	unsigned long fck; | ||||
| 	struct dispc_clock_info dispc_cinfo; | ||||
| 	unsigned long pck; | ||||
| 	int r; | ||||
| 
 | ||||
| 	if (out == NULL || out->manager == NULL) { | ||||
| 		DSSERR("failed to enable display: no output/manager\n"); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	r = regulator_enable(sdi.vdds_sdi_reg); | ||||
| 	if (r) | ||||
| 		goto err_reg_enable; | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_get_dispc; | ||||
| 
 | ||||
| 	/* 15.5.9.1.2 */ | ||||
| 	t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | ||||
| 	t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | ||||
| 
 | ||||
| 	r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo); | ||||
| 	if (r) | ||||
| 		goto err_calc_clock_div; | ||||
| 
 | ||||
| 	sdi.mgr_config.clock_info = dispc_cinfo; | ||||
| 
 | ||||
| 	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; | ||||
| 
 | ||||
| 	if (pck != t->pixelclock) { | ||||
| 		DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n", | ||||
| 			t->pixelclock, pck); | ||||
| 
 | ||||
| 		t->pixelclock = pck; | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	dss_mgr_set_timings(out->manager, t); | ||||
| 
 | ||||
| 	r = dss_set_fck_rate(fck); | ||||
| 	if (r) | ||||
| 		goto err_set_dss_clock_div; | ||||
| 
 | ||||
| 	sdi_config_lcd_manager(dssdev); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * LCLK and PCLK divisors are located in shadow registers, and we | ||||
| 	 * normally write them to DISPC registers when enabling the output. | ||||
| 	 * However, SDI uses pck-free as source clock for its PLL, and pck-free | ||||
| 	 * is affected by the divisors. And as we need the PLL before enabling | ||||
| 	 * the output, we need to write the divisors early. | ||||
| 	 * | ||||
| 	 * It seems just writing to the DISPC register is enough, and we don't | ||||
| 	 * need to care about the shadow register mechanism for pck-free. The | ||||
| 	 * exact reason for this is unknown. | ||||
| 	 */ | ||||
| 	dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info); | ||||
| 
 | ||||
| 	dss_sdi_init(sdi.datapairs); | ||||
| 	r = dss_sdi_enable(); | ||||
| 	if (r) | ||||
| 		goto err_sdi_enable; | ||||
| 	mdelay(2); | ||||
| 
 | ||||
| 	r = dss_mgr_enable(out->manager); | ||||
| 	if (r) | ||||
| 		goto err_mgr_enable; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_mgr_enable: | ||||
| 	dss_sdi_disable(); | ||||
| err_sdi_enable: | ||||
| err_set_dss_clock_div: | ||||
| err_calc_clock_div: | ||||
| 	dispc_runtime_put(); | ||||
| err_get_dispc: | ||||
| 	regulator_disable(sdi.vdds_sdi_reg); | ||||
| err_reg_enable: | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void sdi_display_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = sdi.output.manager; | ||||
| 
 | ||||
| 	dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 	dss_sdi_disable(); | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	regulator_disable(sdi.vdds_sdi_reg); | ||||
| } | ||||
| 
 | ||||
| static void sdi_set_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	sdi.timings = *timings; | ||||
| } | ||||
| 
 | ||||
| static void sdi_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	*timings = sdi.timings; | ||||
| } | ||||
| 
 | ||||
| static int sdi_check_timings(struct omap_dss_device *dssdev, | ||||
| 			struct omap_video_timings *timings) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = sdi.output.manager; | ||||
| 
 | ||||
| 	if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (timings->pixelclock == 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) | ||||
| { | ||||
| 	sdi.datapairs = datapairs; | ||||
| } | ||||
| 
 | ||||
| static int sdi_init_regulator(void) | ||||
| { | ||||
| 	struct regulator *vdds_sdi; | ||||
| 
 | ||||
| 	if (sdi.vdds_sdi_reg) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi"); | ||||
| 	if (IS_ERR(vdds_sdi)) { | ||||
| 		if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER) | ||||
| 			DSSERR("can't get VDDS_SDI regulator\n"); | ||||
| 		return PTR_ERR(vdds_sdi); | ||||
| 	} | ||||
| 
 | ||||
| 	sdi.vdds_sdi_reg = vdds_sdi; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int sdi_connect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = sdi_init_regulator(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||||
| 	if (!mgr) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = dss_mgr_connect(mgr, dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = omapdss_output_set_device(dssdev, dst); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to connect output to new device: %s\n", | ||||
| 				dst->name); | ||||
| 		dss_mgr_disconnect(mgr, dssdev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void sdi_disconnect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	WARN_ON(dst != dssdev->dst); | ||||
| 
 | ||||
| 	if (dst != dssdev->dst) | ||||
| 		return; | ||||
| 
 | ||||
| 	omapdss_output_unset_device(dssdev); | ||||
| 
 | ||||
| 	if (dssdev->manager) | ||||
| 		dss_mgr_disconnect(dssdev->manager, dssdev); | ||||
| } | ||||
| 
 | ||||
| static const struct omapdss_sdi_ops sdi_ops = { | ||||
| 	.connect = sdi_connect, | ||||
| 	.disconnect = sdi_disconnect, | ||||
| 
 | ||||
| 	.enable = sdi_display_enable, | ||||
| 	.disable = sdi_display_disable, | ||||
| 
 | ||||
| 	.check_timings = sdi_check_timings, | ||||
| 	.set_timings = sdi_set_timings, | ||||
| 	.get_timings = sdi_get_timings, | ||||
| 
 | ||||
| 	.set_datapairs = sdi_set_datapairs, | ||||
| }; | ||||
| 
 | ||||
| static void sdi_init_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &sdi.output; | ||||
| 
 | ||||
| 	out->dev = &pdev->dev; | ||||
| 	out->id = OMAP_DSS_OUTPUT_SDI; | ||||
| 	out->output_type = OMAP_DISPLAY_TYPE_SDI; | ||||
| 	out->name = "sdi.0"; | ||||
| 	out->dispc_channel = OMAP_DSS_CHANNEL_LCD; | ||||
| 	out->ops.sdi = &sdi_ops; | ||||
| 	out->owner = THIS_MODULE; | ||||
| 
 | ||||
| 	omapdss_register_output(out); | ||||
| } | ||||
| 
 | ||||
| static void __exit sdi_uninit_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &sdi.output; | ||||
| 
 | ||||
| 	omapdss_unregister_output(out); | ||||
| } | ||||
| 
 | ||||
| static int omap_sdi_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	sdi.pdev = pdev; | ||||
| 
 | ||||
| 	sdi_init_output(pdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __exit omap_sdi_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	sdi_uninit_output(pdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver omap_sdi_driver = { | ||||
| 	.probe		= omap_sdi_probe, | ||||
| 	.remove         = __exit_p(omap_sdi_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_sdi", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init sdi_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&omap_sdi_driver); | ||||
| } | ||||
| 
 | ||||
| void __exit sdi_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omap_sdi_driver); | ||||
| } | ||||
| 
 | ||||
| int __init sdi_init_port(struct platform_device *pdev, struct device_node *port) | ||||
| { | ||||
| 	struct device_node *ep; | ||||
| 	u32 datapairs; | ||||
| 	int r; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_next_endpoint(port, NULL); | ||||
| 	if (!ep) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	r = of_property_read_u32(ep, "datapairs", &datapairs); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to parse datapairs\n"); | ||||
| 		goto err_datapairs; | ||||
| 	} | ||||
| 
 | ||||
| 	sdi.datapairs = datapairs; | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	sdi.pdev = pdev; | ||||
| 
 | ||||
| 	sdi_init_output(pdev); | ||||
| 
 | ||||
| 	sdi.port_initialized = true; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_datapairs: | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| void __exit sdi_uninit_port(void) | ||||
| { | ||||
| 	if (!sdi.port_initialized) | ||||
| 		return; | ||||
| 
 | ||||
| 	sdi_uninit_output(sdi.pdev); | ||||
| } | ||||
							
								
								
									
										981
									
								
								drivers/video/fbdev/omap2/dss/venc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										981
									
								
								drivers/video/fbdev/omap2/dss/venc.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,981 @@ | |||
| /*
 | ||||
|  * linux/drivers/video/omap2/dss/venc.c | ||||
|  * | ||||
|  * Copyright (C) 2009 Nokia Corporation | ||||
|  * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||||
|  * | ||||
|  * VENC settings from TI's DSS driver | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms of the GNU General Public License version 2 as published by | ||||
|  * the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #define DSS_SUBSYS_NAME "VENC" | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/completion.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/string.h> | ||||
| #include <linux/seq_file.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/regulator/consumer.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/of.h> | ||||
| 
 | ||||
| #include <video/omapdss.h> | ||||
| 
 | ||||
| #include "dss.h" | ||||
| #include "dss_features.h" | ||||
| 
 | ||||
| /* Venc registers */ | ||||
| #define VENC_REV_ID				0x00 | ||||
| #define VENC_STATUS				0x04 | ||||
| #define VENC_F_CONTROL				0x08 | ||||
| #define VENC_VIDOUT_CTRL			0x10 | ||||
| #define VENC_SYNC_CTRL				0x14 | ||||
| #define VENC_LLEN				0x1C | ||||
| #define VENC_FLENS				0x20 | ||||
| #define VENC_HFLTR_CTRL				0x24 | ||||
| #define VENC_CC_CARR_WSS_CARR			0x28 | ||||
| #define VENC_C_PHASE				0x2C | ||||
| #define VENC_GAIN_U				0x30 | ||||
| #define VENC_GAIN_V				0x34 | ||||
| #define VENC_GAIN_Y				0x38 | ||||
| #define VENC_BLACK_LEVEL			0x3C | ||||
| #define VENC_BLANK_LEVEL			0x40 | ||||
| #define VENC_X_COLOR				0x44 | ||||
| #define VENC_M_CONTROL				0x48 | ||||
| #define VENC_BSTAMP_WSS_DATA			0x4C | ||||
| #define VENC_S_CARR				0x50 | ||||
| #define VENC_LINE21				0x54 | ||||
| #define VENC_LN_SEL				0x58 | ||||
| #define VENC_L21__WC_CTL			0x5C | ||||
| #define VENC_HTRIGGER_VTRIGGER			0x60 | ||||
| #define VENC_SAVID__EAVID			0x64 | ||||
| #define VENC_FLEN__FAL				0x68 | ||||
| #define VENC_LAL__PHASE_RESET			0x6C | ||||
| #define VENC_HS_INT_START_STOP_X		0x70 | ||||
| #define VENC_HS_EXT_START_STOP_X		0x74 | ||||
| #define VENC_VS_INT_START_X			0x78 | ||||
| #define VENC_VS_INT_STOP_X__VS_INT_START_Y	0x7C | ||||
| #define VENC_VS_INT_STOP_Y__VS_EXT_START_X	0x80 | ||||
| #define VENC_VS_EXT_STOP_X__VS_EXT_START_Y	0x84 | ||||
| #define VENC_VS_EXT_STOP_Y			0x88 | ||||
| #define VENC_AVID_START_STOP_X			0x90 | ||||
| #define VENC_AVID_START_STOP_Y			0x94 | ||||
| #define VENC_FID_INT_START_X__FID_INT_START_Y	0xA0 | ||||
| #define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X	0xA4 | ||||
| #define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y	0xA8 | ||||
| #define VENC_TVDETGP_INT_START_STOP_X		0xB0 | ||||
| #define VENC_TVDETGP_INT_START_STOP_Y		0xB4 | ||||
| #define VENC_GEN_CTRL				0xB8 | ||||
| #define VENC_OUTPUT_CONTROL			0xC4 | ||||
| #define VENC_OUTPUT_TEST			0xC8 | ||||
| #define VENC_DAC_B__DAC_C			0xC8 | ||||
| 
 | ||||
| struct venc_config { | ||||
| 	u32 f_control; | ||||
| 	u32 vidout_ctrl; | ||||
| 	u32 sync_ctrl; | ||||
| 	u32 llen; | ||||
| 	u32 flens; | ||||
| 	u32 hfltr_ctrl; | ||||
| 	u32 cc_carr_wss_carr; | ||||
| 	u32 c_phase; | ||||
| 	u32 gain_u; | ||||
| 	u32 gain_v; | ||||
| 	u32 gain_y; | ||||
| 	u32 black_level; | ||||
| 	u32 blank_level; | ||||
| 	u32 x_color; | ||||
| 	u32 m_control; | ||||
| 	u32 bstamp_wss_data; | ||||
| 	u32 s_carr; | ||||
| 	u32 line21; | ||||
| 	u32 ln_sel; | ||||
| 	u32 l21__wc_ctl; | ||||
| 	u32 htrigger_vtrigger; | ||||
| 	u32 savid__eavid; | ||||
| 	u32 flen__fal; | ||||
| 	u32 lal__phase_reset; | ||||
| 	u32 hs_int_start_stop_x; | ||||
| 	u32 hs_ext_start_stop_x; | ||||
| 	u32 vs_int_start_x; | ||||
| 	u32 vs_int_stop_x__vs_int_start_y; | ||||
| 	u32 vs_int_stop_y__vs_ext_start_x; | ||||
| 	u32 vs_ext_stop_x__vs_ext_start_y; | ||||
| 	u32 vs_ext_stop_y; | ||||
| 	u32 avid_start_stop_x; | ||||
| 	u32 avid_start_stop_y; | ||||
| 	u32 fid_int_start_x__fid_int_start_y; | ||||
| 	u32 fid_int_offset_y__fid_ext_start_x; | ||||
| 	u32 fid_ext_start_y__fid_ext_offset_y; | ||||
| 	u32 tvdetgp_int_start_stop_x; | ||||
| 	u32 tvdetgp_int_start_stop_y; | ||||
| 	u32 gen_ctrl; | ||||
| }; | ||||
| 
 | ||||
| /* from TRM */ | ||||
| static const struct venc_config venc_config_pal_trm = { | ||||
| 	.f_control				= 0, | ||||
| 	.vidout_ctrl				= 1, | ||||
| 	.sync_ctrl				= 0x40, | ||||
| 	.llen					= 0x35F, /* 863 */ | ||||
| 	.flens					= 0x270, /* 624 */ | ||||
| 	.hfltr_ctrl				= 0, | ||||
| 	.cc_carr_wss_carr			= 0x2F7225ED, | ||||
| 	.c_phase				= 0, | ||||
| 	.gain_u					= 0x111, | ||||
| 	.gain_v					= 0x181, | ||||
| 	.gain_y					= 0x140, | ||||
| 	.black_level				= 0x3B, | ||||
| 	.blank_level				= 0x3B, | ||||
| 	.x_color				= 0x7, | ||||
| 	.m_control				= 0x2, | ||||
| 	.bstamp_wss_data			= 0x3F, | ||||
| 	.s_carr					= 0x2A098ACB, | ||||
| 	.line21					= 0, | ||||
| 	.ln_sel					= 0x01290015, | ||||
| 	.l21__wc_ctl				= 0x0000F603, | ||||
| 	.htrigger_vtrigger			= 0, | ||||
| 
 | ||||
| 	.savid__eavid				= 0x06A70108, | ||||
| 	.flen__fal				= 0x00180270, | ||||
| 	.lal__phase_reset			= 0x00040135, | ||||
| 	.hs_int_start_stop_x			= 0x00880358, | ||||
| 	.hs_ext_start_stop_x			= 0x000F035F, | ||||
| 	.vs_int_start_x				= 0x01A70000, | ||||
| 	.vs_int_stop_x__vs_int_start_y		= 0x000001A7, | ||||
| 	.vs_int_stop_y__vs_ext_start_x		= 0x01AF0000, | ||||
| 	.vs_ext_stop_x__vs_ext_start_y		= 0x000101AF, | ||||
| 	.vs_ext_stop_y				= 0x00000025, | ||||
| 	.avid_start_stop_x			= 0x03530083, | ||||
| 	.avid_start_stop_y			= 0x026C002E, | ||||
| 	.fid_int_start_x__fid_int_start_y	= 0x0001008A, | ||||
| 	.fid_int_offset_y__fid_ext_start_x	= 0x002E0138, | ||||
| 	.fid_ext_start_y__fid_ext_offset_y	= 0x01380001, | ||||
| 
 | ||||
| 	.tvdetgp_int_start_stop_x		= 0x00140001, | ||||
| 	.tvdetgp_int_start_stop_y		= 0x00010001, | ||||
| 	.gen_ctrl				= 0x00FF0000, | ||||
| }; | ||||
| 
 | ||||
| /* from TRM */ | ||||
| static const struct venc_config venc_config_ntsc_trm = { | ||||
| 	.f_control				= 0, | ||||
| 	.vidout_ctrl				= 1, | ||||
| 	.sync_ctrl				= 0x8040, | ||||
| 	.llen					= 0x359, | ||||
| 	.flens					= 0x20C, | ||||
| 	.hfltr_ctrl				= 0, | ||||
| 	.cc_carr_wss_carr			= 0x043F2631, | ||||
| 	.c_phase				= 0, | ||||
| 	.gain_u					= 0x102, | ||||
| 	.gain_v					= 0x16C, | ||||
| 	.gain_y					= 0x12F, | ||||
| 	.black_level				= 0x43, | ||||
| 	.blank_level				= 0x38, | ||||
| 	.x_color				= 0x7, | ||||
| 	.m_control				= 0x1, | ||||
| 	.bstamp_wss_data			= 0x38, | ||||
| 	.s_carr					= 0x21F07C1F, | ||||
| 	.line21					= 0, | ||||
| 	.ln_sel					= 0x01310011, | ||||
| 	.l21__wc_ctl				= 0x0000F003, | ||||
| 	.htrigger_vtrigger			= 0, | ||||
| 
 | ||||
| 	.savid__eavid				= 0x069300F4, | ||||
| 	.flen__fal				= 0x0016020C, | ||||
| 	.lal__phase_reset			= 0x00060107, | ||||
| 	.hs_int_start_stop_x			= 0x008E0350, | ||||
| 	.hs_ext_start_stop_x			= 0x000F0359, | ||||
| 	.vs_int_start_x				= 0x01A00000, | ||||
| 	.vs_int_stop_x__vs_int_start_y		= 0x020701A0, | ||||
| 	.vs_int_stop_y__vs_ext_start_x		= 0x01AC0024, | ||||
| 	.vs_ext_stop_x__vs_ext_start_y		= 0x020D01AC, | ||||
| 	.vs_ext_stop_y				= 0x00000006, | ||||
| 	.avid_start_stop_x			= 0x03480078, | ||||
| 	.avid_start_stop_y			= 0x02060024, | ||||
| 	.fid_int_start_x__fid_int_start_y	= 0x0001008A, | ||||
| 	.fid_int_offset_y__fid_ext_start_x	= 0x01AC0106, | ||||
| 	.fid_ext_start_y__fid_ext_offset_y	= 0x01060006, | ||||
| 
 | ||||
| 	.tvdetgp_int_start_stop_x		= 0x00140001, | ||||
| 	.tvdetgp_int_start_stop_y		= 0x00010001, | ||||
| 	.gen_ctrl				= 0x00F90000, | ||||
| }; | ||||
| 
 | ||||
| static const struct venc_config venc_config_pal_bdghi = { | ||||
| 	.f_control				= 0, | ||||
| 	.vidout_ctrl				= 0, | ||||
| 	.sync_ctrl				= 0, | ||||
| 	.hfltr_ctrl				= 0, | ||||
| 	.x_color				= 0, | ||||
| 	.line21					= 0, | ||||
| 	.ln_sel					= 21, | ||||
| 	.htrigger_vtrigger			= 0, | ||||
| 	.tvdetgp_int_start_stop_x		= 0x00140001, | ||||
| 	.tvdetgp_int_start_stop_y		= 0x00010001, | ||||
| 	.gen_ctrl				= 0x00FB0000, | ||||
| 
 | ||||
| 	.llen					= 864-1, | ||||
| 	.flens					= 625-1, | ||||
| 	.cc_carr_wss_carr			= 0x2F7625ED, | ||||
| 	.c_phase				= 0xDF, | ||||
| 	.gain_u					= 0x111, | ||||
| 	.gain_v					= 0x181, | ||||
| 	.gain_y					= 0x140, | ||||
| 	.black_level				= 0x3e, | ||||
| 	.blank_level				= 0x3e, | ||||
| 	.m_control				= 0<<2 | 1<<1, | ||||
| 	.bstamp_wss_data			= 0x42, | ||||
| 	.s_carr					= 0x2a098acb, | ||||
| 	.l21__wc_ctl				= 0<<13 | 0x16<<8 | 0<<0, | ||||
| 	.savid__eavid				= 0x06A70108, | ||||
| 	.flen__fal				= 23<<16 | 624<<0, | ||||
| 	.lal__phase_reset			= 2<<17 | 310<<0, | ||||
| 	.hs_int_start_stop_x			= 0x00920358, | ||||
| 	.hs_ext_start_stop_x			= 0x000F035F, | ||||
| 	.vs_int_start_x				= 0x1a7<<16, | ||||
| 	.vs_int_stop_x__vs_int_start_y		= 0x000601A7, | ||||
| 	.vs_int_stop_y__vs_ext_start_x		= 0x01AF0036, | ||||
| 	.vs_ext_stop_x__vs_ext_start_y		= 0x27101af, | ||||
| 	.vs_ext_stop_y				= 0x05, | ||||
| 	.avid_start_stop_x			= 0x03530082, | ||||
| 	.avid_start_stop_y			= 0x0270002E, | ||||
| 	.fid_int_start_x__fid_int_start_y	= 0x0005008A, | ||||
| 	.fid_int_offset_y__fid_ext_start_x	= 0x002E0138, | ||||
| 	.fid_ext_start_y__fid_ext_offset_y	= 0x01380005, | ||||
| }; | ||||
| 
 | ||||
| const struct omap_video_timings omap_dss_pal_timings = { | ||||
| 	.x_res		= 720, | ||||
| 	.y_res		= 574, | ||||
| 	.pixelclock	= 13500000, | ||||
| 	.hsw		= 64, | ||||
| 	.hfp		= 12, | ||||
| 	.hbp		= 68, | ||||
| 	.vsw		= 5, | ||||
| 	.vfp		= 5, | ||||
| 	.vbp		= 41, | ||||
| 
 | ||||
| 	.interlace	= true, | ||||
| }; | ||||
| EXPORT_SYMBOL(omap_dss_pal_timings); | ||||
| 
 | ||||
| const struct omap_video_timings omap_dss_ntsc_timings = { | ||||
| 	.x_res		= 720, | ||||
| 	.y_res		= 482, | ||||
| 	.pixelclock	= 13500000, | ||||
| 	.hsw		= 64, | ||||
| 	.hfp		= 16, | ||||
| 	.hbp		= 58, | ||||
| 	.vsw		= 6, | ||||
| 	.vfp		= 6, | ||||
| 	.vbp		= 31, | ||||
| 
 | ||||
| 	.interlace	= true, | ||||
| }; | ||||
| EXPORT_SYMBOL(omap_dss_ntsc_timings); | ||||
| 
 | ||||
| static struct { | ||||
| 	struct platform_device *pdev; | ||||
| 	void __iomem *base; | ||||
| 	struct mutex venc_lock; | ||||
| 	u32 wss_data; | ||||
| 	struct regulator *vdda_dac_reg; | ||||
| 
 | ||||
| 	struct clk	*tv_dac_clk; | ||||
| 
 | ||||
| 	struct omap_video_timings timings; | ||||
| 	enum omap_dss_venc_type type; | ||||
| 	bool invert_polarity; | ||||
| 
 | ||||
| 	struct omap_dss_device output; | ||||
| } venc; | ||||
| 
 | ||||
| static inline void venc_write_reg(int idx, u32 val) | ||||
| { | ||||
| 	__raw_writel(val, venc.base + idx); | ||||
| } | ||||
| 
 | ||||
| static inline u32 venc_read_reg(int idx) | ||||
| { | ||||
| 	u32 l = __raw_readl(venc.base + idx); | ||||
| 	return l; | ||||
| } | ||||
| 
 | ||||
| static void venc_write_config(const struct venc_config *config) | ||||
| { | ||||
| 	DSSDBG("write venc conf\n"); | ||||
| 
 | ||||
| 	venc_write_reg(VENC_LLEN, config->llen); | ||||
| 	venc_write_reg(VENC_FLENS, config->flens); | ||||
| 	venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); | ||||
| 	venc_write_reg(VENC_C_PHASE, config->c_phase); | ||||
| 	venc_write_reg(VENC_GAIN_U, config->gain_u); | ||||
| 	venc_write_reg(VENC_GAIN_V, config->gain_v); | ||||
| 	venc_write_reg(VENC_GAIN_Y, config->gain_y); | ||||
| 	venc_write_reg(VENC_BLACK_LEVEL, config->black_level); | ||||
| 	venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); | ||||
| 	venc_write_reg(VENC_M_CONTROL, config->m_control); | ||||
| 	venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | | ||||
| 			venc.wss_data); | ||||
| 	venc_write_reg(VENC_S_CARR, config->s_carr); | ||||
| 	venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); | ||||
| 	venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); | ||||
| 	venc_write_reg(VENC_FLEN__FAL, config->flen__fal); | ||||
| 	venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); | ||||
| 	venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); | ||||
| 	venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); | ||||
| 	venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); | ||||
| 	venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, | ||||
| 		       config->vs_int_stop_x__vs_int_start_y); | ||||
| 	venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, | ||||
| 		       config->vs_int_stop_y__vs_ext_start_x); | ||||
| 	venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, | ||||
| 		       config->vs_ext_stop_x__vs_ext_start_y); | ||||
| 	venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); | ||||
| 	venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); | ||||
| 	venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); | ||||
| 	venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, | ||||
| 		       config->fid_int_start_x__fid_int_start_y); | ||||
| 	venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, | ||||
| 		       config->fid_int_offset_y__fid_ext_start_x); | ||||
| 	venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, | ||||
| 		       config->fid_ext_start_y__fid_ext_offset_y); | ||||
| 
 | ||||
| 	venc_write_reg(VENC_DAC_B__DAC_C,  venc_read_reg(VENC_DAC_B__DAC_C)); | ||||
| 	venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); | ||||
| 	venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); | ||||
| 	venc_write_reg(VENC_X_COLOR, config->x_color); | ||||
| 	venc_write_reg(VENC_LINE21, config->line21); | ||||
| 	venc_write_reg(VENC_LN_SEL, config->ln_sel); | ||||
| 	venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); | ||||
| 	venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, | ||||
| 		       config->tvdetgp_int_start_stop_x); | ||||
| 	venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, | ||||
| 		       config->tvdetgp_int_start_stop_y); | ||||
| 	venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); | ||||
| 	venc_write_reg(VENC_F_CONTROL, config->f_control); | ||||
| 	venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); | ||||
| } | ||||
| 
 | ||||
| static void venc_reset(void) | ||||
| { | ||||
| 	int t = 1000; | ||||
| 
 | ||||
| 	venc_write_reg(VENC_F_CONTROL, 1<<8); | ||||
| 	while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { | ||||
| 		if (--t == 0) { | ||||
| 			DSSERR("Failed to reset venc\n"); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET | ||||
| 	/* the magical sleep that makes things work */ | ||||
| 	/* XXX more info? What bug this circumvents? */ | ||||
| 	msleep(20); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static int venc_runtime_get(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("venc_runtime_get\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_get_sync(&venc.pdev->dev); | ||||
| 	WARN_ON(r < 0); | ||||
| 	return r < 0 ? r : 0; | ||||
| } | ||||
| 
 | ||||
| static void venc_runtime_put(void) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("venc_runtime_put\n"); | ||||
| 
 | ||||
| 	r = pm_runtime_put_sync(&venc.pdev->dev); | ||||
| 	WARN_ON(r < 0 && r != -ENOSYS); | ||||
| } | ||||
| 
 | ||||
| static const struct venc_config *venc_timings_to_config( | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) | ||||
| 		return &venc_config_pal_trm; | ||||
| 
 | ||||
| 	if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) | ||||
| 		return &venc_config_ntsc_trm; | ||||
| 
 | ||||
| 	BUG(); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static int venc_power_on(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = venc.output.manager; | ||||
| 	u32 l; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = venc_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err0; | ||||
| 
 | ||||
| 	venc_reset(); | ||||
| 	venc_write_config(venc_timings_to_config(&venc.timings)); | ||||
| 
 | ||||
| 	dss_set_venc_output(venc.type); | ||||
| 	dss_set_dac_pwrdn_bgz(1); | ||||
| 
 | ||||
| 	l = 0; | ||||
| 
 | ||||
| 	if (venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) | ||||
| 		l |= 1 << 1; | ||||
| 	else /* S-Video */ | ||||
| 		l |= (1 << 0) | (1 << 2); | ||||
| 
 | ||||
| 	if (venc.invert_polarity == false) | ||||
| 		l |= 1 << 3; | ||||
| 
 | ||||
| 	venc_write_reg(VENC_OUTPUT_CONTROL, l); | ||||
| 
 | ||||
| 	dss_mgr_set_timings(mgr, &venc.timings); | ||||
| 
 | ||||
| 	r = regulator_enable(venc.vdda_dac_reg); | ||||
| 	if (r) | ||||
| 		goto err1; | ||||
| 
 | ||||
| 	r = dss_mgr_enable(mgr); | ||||
| 	if (r) | ||||
| 		goto err2; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err2: | ||||
| 	regulator_disable(venc.vdda_dac_reg); | ||||
| err1: | ||||
| 	venc_write_reg(VENC_OUTPUT_CONTROL, 0); | ||||
| 	dss_set_dac_pwrdn_bgz(0); | ||||
| 
 | ||||
| 	venc_runtime_put(); | ||||
| err0: | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void venc_power_off(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr = venc.output.manager; | ||||
| 
 | ||||
| 	venc_write_reg(VENC_OUTPUT_CONTROL, 0); | ||||
| 	dss_set_dac_pwrdn_bgz(0); | ||||
| 
 | ||||
| 	dss_mgr_disable(mgr); | ||||
| 
 | ||||
| 	regulator_disable(venc.vdda_dac_reg); | ||||
| 
 | ||||
| 	venc_runtime_put(); | ||||
| } | ||||
| 
 | ||||
| static int venc_display_enable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &venc.output; | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("venc_display_enable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	if (out == NULL || out->manager == NULL) { | ||||
| 		DSSERR("Failed to enable display: no output/manager\n"); | ||||
| 		r = -ENODEV; | ||||
| 		goto err0; | ||||
| 	} | ||||
| 
 | ||||
| 	r = venc_power_on(dssdev); | ||||
| 	if (r) | ||||
| 		goto err0; | ||||
| 
 | ||||
| 	venc.wss_data = 0; | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| err0: | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void venc_display_disable(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	DSSDBG("venc_display_disable\n"); | ||||
| 
 | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	venc_power_off(dssdev); | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| } | ||||
| 
 | ||||
| static void venc_set_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	DSSDBG("venc_set_timings\n"); | ||||
| 
 | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	/* Reset WSS data when the TV standard changes. */ | ||||
| 	if (memcmp(&venc.timings, timings, sizeof(*timings))) | ||||
| 		venc.wss_data = 0; | ||||
| 
 | ||||
| 	venc.timings = *timings; | ||||
| 
 | ||||
| 	dispc_set_tv_pclk(13500000); | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| } | ||||
| 
 | ||||
| static int venc_check_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	DSSDBG("venc_check_timings\n"); | ||||
| 
 | ||||
| 	if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static void venc_get_timings(struct omap_dss_device *dssdev, | ||||
| 		struct omap_video_timings *timings) | ||||
| { | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	*timings = venc.timings; | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| } | ||||
| 
 | ||||
| static u32 venc_get_wss(struct omap_dss_device *dssdev) | ||||
| { | ||||
| 	/* Invert due to VENC_L21_WC_CTL:INV=1 */ | ||||
| 	return (venc.wss_data >> 8) ^ 0xfffff; | ||||
| } | ||||
| 
 | ||||
| static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) | ||||
| { | ||||
| 	const struct venc_config *config; | ||||
| 	int r; | ||||
| 
 | ||||
| 	DSSDBG("venc_set_wss\n"); | ||||
| 
 | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	config = venc_timings_to_config(&venc.timings); | ||||
| 
 | ||||
| 	/* Invert due to VENC_L21_WC_CTL:INV=1 */ | ||||
| 	venc.wss_data = (wss ^ 0xfffff) << 8; | ||||
| 
 | ||||
| 	r = venc_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | | ||||
| 			venc.wss_data); | ||||
| 
 | ||||
| 	venc_runtime_put(); | ||||
| 
 | ||||
| err: | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static void venc_set_type(struct omap_dss_device *dssdev, | ||||
| 		enum omap_dss_venc_type type) | ||||
| { | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	venc.type = type; | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| } | ||||
| 
 | ||||
| static void venc_invert_vid_out_polarity(struct omap_dss_device *dssdev, | ||||
| 		bool invert_polarity) | ||||
| { | ||||
| 	mutex_lock(&venc.venc_lock); | ||||
| 
 | ||||
| 	venc.invert_polarity = invert_polarity; | ||||
| 
 | ||||
| 	mutex_unlock(&venc.venc_lock); | ||||
| } | ||||
| 
 | ||||
| static int venc_init_regulator(void) | ||||
| { | ||||
| 	struct regulator *vdda_dac; | ||||
| 
 | ||||
| 	if (venc.vdda_dac_reg != NULL) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (venc.pdev->dev.of_node) | ||||
| 		vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda"); | ||||
| 	else | ||||
| 		vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac"); | ||||
| 
 | ||||
| 	if (IS_ERR(vdda_dac)) { | ||||
| 		if (PTR_ERR(vdda_dac) != -EPROBE_DEFER) | ||||
| 			DSSERR("can't get VDDA_DAC regulator\n"); | ||||
| 		return PTR_ERR(vdda_dac); | ||||
| 	} | ||||
| 
 | ||||
| 	venc.vdda_dac_reg = vdda_dac; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void venc_dump_regs(struct seq_file *s) | ||||
| { | ||||
| #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) | ||||
| 
 | ||||
| 	if (venc_runtime_get()) | ||||
| 		return; | ||||
| 
 | ||||
| 	DUMPREG(VENC_F_CONTROL); | ||||
| 	DUMPREG(VENC_VIDOUT_CTRL); | ||||
| 	DUMPREG(VENC_SYNC_CTRL); | ||||
| 	DUMPREG(VENC_LLEN); | ||||
| 	DUMPREG(VENC_FLENS); | ||||
| 	DUMPREG(VENC_HFLTR_CTRL); | ||||
| 	DUMPREG(VENC_CC_CARR_WSS_CARR); | ||||
| 	DUMPREG(VENC_C_PHASE); | ||||
| 	DUMPREG(VENC_GAIN_U); | ||||
| 	DUMPREG(VENC_GAIN_V); | ||||
| 	DUMPREG(VENC_GAIN_Y); | ||||
| 	DUMPREG(VENC_BLACK_LEVEL); | ||||
| 	DUMPREG(VENC_BLANK_LEVEL); | ||||
| 	DUMPREG(VENC_X_COLOR); | ||||
| 	DUMPREG(VENC_M_CONTROL); | ||||
| 	DUMPREG(VENC_BSTAMP_WSS_DATA); | ||||
| 	DUMPREG(VENC_S_CARR); | ||||
| 	DUMPREG(VENC_LINE21); | ||||
| 	DUMPREG(VENC_LN_SEL); | ||||
| 	DUMPREG(VENC_L21__WC_CTL); | ||||
| 	DUMPREG(VENC_HTRIGGER_VTRIGGER); | ||||
| 	DUMPREG(VENC_SAVID__EAVID); | ||||
| 	DUMPREG(VENC_FLEN__FAL); | ||||
| 	DUMPREG(VENC_LAL__PHASE_RESET); | ||||
| 	DUMPREG(VENC_HS_INT_START_STOP_X); | ||||
| 	DUMPREG(VENC_HS_EXT_START_STOP_X); | ||||
| 	DUMPREG(VENC_VS_INT_START_X); | ||||
| 	DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y); | ||||
| 	DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X); | ||||
| 	DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y); | ||||
| 	DUMPREG(VENC_VS_EXT_STOP_Y); | ||||
| 	DUMPREG(VENC_AVID_START_STOP_X); | ||||
| 	DUMPREG(VENC_AVID_START_STOP_Y); | ||||
| 	DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y); | ||||
| 	DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); | ||||
| 	DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); | ||||
| 	DUMPREG(VENC_TVDETGP_INT_START_STOP_X); | ||||
| 	DUMPREG(VENC_TVDETGP_INT_START_STOP_Y); | ||||
| 	DUMPREG(VENC_GEN_CTRL); | ||||
| 	DUMPREG(VENC_OUTPUT_CONTROL); | ||||
| 	DUMPREG(VENC_OUTPUT_TEST); | ||||
| 
 | ||||
| 	venc_runtime_put(); | ||||
| 
 | ||||
| #undef DUMPREG | ||||
| } | ||||
| 
 | ||||
| static int venc_get_clocks(struct platform_device *pdev) | ||||
| { | ||||
| 	struct clk *clk; | ||||
| 
 | ||||
| 	if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) { | ||||
| 		clk = devm_clk_get(&pdev->dev, "tv_dac_clk"); | ||||
| 		if (IS_ERR(clk)) { | ||||
| 			DSSERR("can't get tv_dac_clk\n"); | ||||
| 			return PTR_ERR(clk); | ||||
| 		} | ||||
| 	} else { | ||||
| 		clk = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	venc.tv_dac_clk = clk; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int venc_connect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	struct omap_overlay_manager *mgr; | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = venc_init_regulator(); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||||
| 	if (!mgr) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	r = dss_mgr_connect(mgr, dssdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	r = omapdss_output_set_device(dssdev, dst); | ||||
| 	if (r) { | ||||
| 		DSSERR("failed to connect output to new device: %s\n", | ||||
| 				dst->name); | ||||
| 		dss_mgr_disconnect(mgr, dssdev); | ||||
| 		return r; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void venc_disconnect(struct omap_dss_device *dssdev, | ||||
| 		struct omap_dss_device *dst) | ||||
| { | ||||
| 	WARN_ON(dst != dssdev->dst); | ||||
| 
 | ||||
| 	if (dst != dssdev->dst) | ||||
| 		return; | ||||
| 
 | ||||
| 	omapdss_output_unset_device(dssdev); | ||||
| 
 | ||||
| 	if (dssdev->manager) | ||||
| 		dss_mgr_disconnect(dssdev->manager, dssdev); | ||||
| } | ||||
| 
 | ||||
| static const struct omapdss_atv_ops venc_ops = { | ||||
| 	.connect = venc_connect, | ||||
| 	.disconnect = venc_disconnect, | ||||
| 
 | ||||
| 	.enable = venc_display_enable, | ||||
| 	.disable = venc_display_disable, | ||||
| 
 | ||||
| 	.check_timings = venc_check_timings, | ||||
| 	.set_timings = venc_set_timings, | ||||
| 	.get_timings = venc_get_timings, | ||||
| 
 | ||||
| 	.set_type = venc_set_type, | ||||
| 	.invert_vid_out_polarity = venc_invert_vid_out_polarity, | ||||
| 
 | ||||
| 	.set_wss = venc_set_wss, | ||||
| 	.get_wss = venc_get_wss, | ||||
| }; | ||||
| 
 | ||||
| static void venc_init_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &venc.output; | ||||
| 
 | ||||
| 	out->dev = &pdev->dev; | ||||
| 	out->id = OMAP_DSS_OUTPUT_VENC; | ||||
| 	out->output_type = OMAP_DISPLAY_TYPE_VENC; | ||||
| 	out->name = "venc.0"; | ||||
| 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | ||||
| 	out->ops.atv = &venc_ops; | ||||
| 	out->owner = THIS_MODULE; | ||||
| 
 | ||||
| 	omapdss_register_output(out); | ||||
| } | ||||
| 
 | ||||
| static void __exit venc_uninit_output(struct platform_device *pdev) | ||||
| { | ||||
| 	struct omap_dss_device *out = &venc.output; | ||||
| 
 | ||||
| 	omapdss_unregister_output(out); | ||||
| } | ||||
| 
 | ||||
| static int venc_probe_of(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *node = pdev->dev.of_node; | ||||
| 	struct device_node *ep; | ||||
| 	u32 channels; | ||||
| 	int r; | ||||
| 
 | ||||
| 	ep = omapdss_of_get_first_endpoint(node); | ||||
| 	if (!ep) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	venc.invert_polarity = of_property_read_bool(ep, "ti,invert-polarity"); | ||||
| 
 | ||||
| 	r = of_property_read_u32(ep, "ti,channels", &channels); | ||||
| 	if (r) { | ||||
| 		dev_err(&pdev->dev, | ||||
| 			"failed to read property 'ti,channels': %d\n", r); | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (channels) { | ||||
| 	case 1: | ||||
| 		venc.type = OMAP_DSS_VENC_TYPE_COMPOSITE; | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		venc.type = OMAP_DSS_VENC_TYPE_SVIDEO; | ||||
| 		break; | ||||
| 	default: | ||||
| 		dev_err(&pdev->dev, "bad channel propert '%d'\n", channels); | ||||
| 		r = -EINVAL; | ||||
| 		goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	return 0; | ||||
| err: | ||||
| 	of_node_put(ep); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* VENC HW IP initialisation */ | ||||
| static int omap_venchw_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	u8 rev_id; | ||||
| 	struct resource *venc_mem; | ||||
| 	int r; | ||||
| 
 | ||||
| 	venc.pdev = pdev; | ||||
| 
 | ||||
| 	mutex_init(&venc.venc_lock); | ||||
| 
 | ||||
| 	venc.wss_data = 0; | ||||
| 
 | ||||
| 	venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!venc_mem) { | ||||
| 		DSSERR("can't get IORESOURCE_MEM VENC\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	venc.base = devm_ioremap(&pdev->dev, venc_mem->start, | ||||
| 				 resource_size(venc_mem)); | ||||
| 	if (!venc.base) { | ||||
| 		DSSERR("can't ioremap VENC\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	r = venc_get_clocks(pdev); | ||||
| 	if (r) | ||||
| 		return r; | ||||
| 
 | ||||
| 	pm_runtime_enable(&pdev->dev); | ||||
| 
 | ||||
| 	r = venc_runtime_get(); | ||||
| 	if (r) | ||||
| 		goto err_runtime_get; | ||||
| 
 | ||||
| 	rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); | ||||
| 	dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); | ||||
| 
 | ||||
| 	venc_runtime_put(); | ||||
| 
 | ||||
| 	if (pdev->dev.of_node) { | ||||
| 		r = venc_probe_of(pdev); | ||||
| 		if (r) { | ||||
| 			DSSERR("Invalid DT data\n"); | ||||
| 			goto err_probe_of; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dss_debugfs_create_file("venc", venc_dump_regs); | ||||
| 
 | ||||
| 	venc_init_output(pdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_probe_of: | ||||
| err_runtime_get: | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| 	return r; | ||||
| } | ||||
| 
 | ||||
| static int __exit omap_venchw_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	venc_uninit_output(pdev); | ||||
| 
 | ||||
| 	pm_runtime_disable(&pdev->dev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int venc_runtime_suspend(struct device *dev) | ||||
| { | ||||
| 	if (venc.tv_dac_clk) | ||||
| 		clk_disable_unprepare(venc.tv_dac_clk); | ||||
| 
 | ||||
| 	dispc_runtime_put(); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int venc_runtime_resume(struct device *dev) | ||||
| { | ||||
| 	int r; | ||||
| 
 | ||||
| 	r = dispc_runtime_get(); | ||||
| 	if (r < 0) | ||||
| 		return r; | ||||
| 
 | ||||
| 	if (venc.tv_dac_clk) | ||||
| 		clk_prepare_enable(venc.tv_dac_clk); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops venc_pm_ops = { | ||||
| 	.runtime_suspend = venc_runtime_suspend, | ||||
| 	.runtime_resume = venc_runtime_resume, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static const struct of_device_id venc_of_match[] = { | ||||
| 	{ .compatible = "ti,omap2-venc", }, | ||||
| 	{ .compatible = "ti,omap3-venc", }, | ||||
| 	{ .compatible = "ti,omap4-venc", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver omap_venchw_driver = { | ||||
| 	.probe		= omap_venchw_probe, | ||||
| 	.remove         = __exit_p(omap_venchw_remove), | ||||
| 	.driver         = { | ||||
| 		.name   = "omapdss_venc", | ||||
| 		.owner  = THIS_MODULE, | ||||
| 		.pm	= &venc_pm_ops, | ||||
| 		.of_match_table = venc_of_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| int __init venc_init_platform_driver(void) | ||||
| { | ||||
| 	return platform_driver_register(&omap_venchw_driver); | ||||
| } | ||||
| 
 | ||||
| void __exit venc_uninit_platform_driver(void) | ||||
| { | ||||
| 	platform_driver_unregister(&omap_venchw_driver); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228