mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/tegra: dpaux: Add pinctrl support
The DPAUX pins are shared with an internal I2C controller. To allow these pins to be muxed to the I2C controller, register a pinctrl device for the DPAUX device. This is based upon work by Thierry Reding <treding@nvidia.com>. Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
		
							parent
							
								
									6cb68e46a9
								
							
						
					
					
						commit
						0751bb5c44
					
				
					 1 changed files with 120 additions and 3 deletions
				
			
		| 
						 | 
					@ -12,6 +12,9 @@
 | 
				
			||||||
#include <linux/interrupt.h>
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
#include <linux/of_gpio.h>
 | 
					#include <linux/of_gpio.h>
 | 
				
			||||||
 | 
					#include <linux/pinctrl/pinconf-generic.h>
 | 
				
			||||||
 | 
					#include <linux/pinctrl/pinctrl.h>
 | 
				
			||||||
 | 
					#include <linux/pinctrl/pinmux.h>
 | 
				
			||||||
#include <linux/platform_device.h>
 | 
					#include <linux/platform_device.h>
 | 
				
			||||||
#include <linux/reset.h>
 | 
					#include <linux/reset.h>
 | 
				
			||||||
#include <linux/regulator/consumer.h>
 | 
					#include <linux/regulator/consumer.h>
 | 
				
			||||||
| 
						 | 
					@ -44,6 +47,11 @@ struct tegra_dpaux {
 | 
				
			||||||
	struct completion complete;
 | 
						struct completion complete;
 | 
				
			||||||
	struct work_struct work;
 | 
						struct work_struct work;
 | 
				
			||||||
	struct list_head list;
 | 
						struct list_head list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_GENERIC_PINCONF
 | 
				
			||||||
 | 
						struct pinctrl_dev *pinctrl;
 | 
				
			||||||
 | 
						struct pinctrl_desc desc;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
 | 
					static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
 | 
				
			||||||
| 
						 | 
					@ -267,6 +275,12 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum tegra_dpaux_functions {
 | 
				
			||||||
 | 
						DPAUX_PADCTL_FUNC_AUX,
 | 
				
			||||||
 | 
						DPAUX_PADCTL_FUNC_I2C,
 | 
				
			||||||
 | 
						DPAUX_PADCTL_FUNC_OFF,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux)
 | 
					static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
 | 
						u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE);
 | 
				
			||||||
| 
						 | 
					@ -290,7 +304,7 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
 | 
				
			||||||
	u32 value;
 | 
						u32 value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (function) {
 | 
						switch (function) {
 | 
				
			||||||
	case DPAUX_HYBRID_PADCTL_MODE_AUX:
 | 
						case DPAUX_PADCTL_FUNC_AUX:
 | 
				
			||||||
		value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
 | 
							value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) |
 | 
				
			||||||
			DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
 | 
								DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) |
 | 
				
			||||||
			DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
 | 
								DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) |
 | 
				
			||||||
| 
						 | 
					@ -298,12 +312,16 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
 | 
				
			||||||
			DPAUX_HYBRID_PADCTL_MODE_AUX;
 | 
								DPAUX_HYBRID_PADCTL_MODE_AUX;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case DPAUX_HYBRID_PADCTL_MODE_I2C:
 | 
						case DPAUX_PADCTL_FUNC_I2C:
 | 
				
			||||||
		value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
 | 
							value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV |
 | 
				
			||||||
			DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
 | 
								DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
 | 
				
			||||||
			DPAUX_HYBRID_PADCTL_MODE_I2C;
 | 
								DPAUX_HYBRID_PADCTL_MODE_I2C;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case DPAUX_PADCTL_FUNC_OFF:
 | 
				
			||||||
 | 
							tegra_dpaux_pad_power_down(dpaux);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return -ENOTSUPP;
 | 
							return -ENOTSUPP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -314,6 +332,91 @@ static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_GENERIC_PINCONF
 | 
				
			||||||
 | 
					static const struct pinctrl_pin_desc tegra_dpaux_pins[] = {
 | 
				
			||||||
 | 
						PINCTRL_PIN(0, "DP_AUX_CHx_P"),
 | 
				
			||||||
 | 
						PINCTRL_PIN(1, "DP_AUX_CHx_N"),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char * const tegra_dpaux_groups[] = {
 | 
				
			||||||
 | 
						"dpaux-io",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char * const tegra_dpaux_functions[] = {
 | 
				
			||||||
 | 
						"aux",
 | 
				
			||||||
 | 
						"i2c",
 | 
				
			||||||
 | 
						"off",
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ARRAY_SIZE(tegra_dpaux_groups);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl,
 | 
				
			||||||
 | 
										      unsigned int group)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return tegra_dpaux_groups[group];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl,
 | 
				
			||||||
 | 
									      unsigned group, const unsigned **pins,
 | 
				
			||||||
 | 
									      unsigned *num_pins)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*pins = tegra_dpaux_pin_numbers;
 | 
				
			||||||
 | 
						*num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pinctrl_ops tegra_dpaux_pinctrl_ops = {
 | 
				
			||||||
 | 
						.get_groups_count = tegra_dpaux_get_groups_count,
 | 
				
			||||||
 | 
						.get_group_name = tegra_dpaux_get_group_name,
 | 
				
			||||||
 | 
						.get_group_pins = tegra_dpaux_get_group_pins,
 | 
				
			||||||
 | 
						.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
 | 
				
			||||||
 | 
						.dt_free_map = pinconf_generic_dt_free_map,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ARRAY_SIZE(tegra_dpaux_functions);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl,
 | 
				
			||||||
 | 
											 unsigned int function)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return tegra_dpaux_functions[function];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl,
 | 
				
			||||||
 | 
										   unsigned int function,
 | 
				
			||||||
 | 
										   const char * const **groups,
 | 
				
			||||||
 | 
										   unsigned * const num_groups)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*num_groups = ARRAY_SIZE(tegra_dpaux_groups);
 | 
				
			||||||
 | 
						*groups = tegra_dpaux_groups;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl,
 | 
				
			||||||
 | 
								       unsigned int function, unsigned int group)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pinctrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tegra_dpaux_pad_config(dpaux, function);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct pinmux_ops tegra_dpaux_pinmux_ops = {
 | 
				
			||||||
 | 
						.get_functions_count = tegra_dpaux_get_functions_count,
 | 
				
			||||||
 | 
						.get_function_name = tegra_dpaux_get_function_name,
 | 
				
			||||||
 | 
						.get_function_groups = tegra_dpaux_get_function_groups,
 | 
				
			||||||
 | 
						.set_mux = tegra_dpaux_set_mux,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tegra_dpaux_probe(struct platform_device *pdev)
 | 
					static int tegra_dpaux_probe(struct platform_device *pdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tegra_dpaux *dpaux;
 | 
						struct tegra_dpaux *dpaux;
 | 
				
			||||||
| 
						 | 
					@ -427,6 +530,20 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_GENERIC_PINCONF
 | 
				
			||||||
 | 
						dpaux->desc.name = dev_name(&pdev->dev);
 | 
				
			||||||
 | 
						dpaux->desc.pins = tegra_dpaux_pins;
 | 
				
			||||||
 | 
						dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins);
 | 
				
			||||||
 | 
						dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops;
 | 
				
			||||||
 | 
						dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops;
 | 
				
			||||||
 | 
						dpaux->desc.owner = THIS_MODULE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux);
 | 
				
			||||||
 | 
						if (!dpaux->pinctrl) {
 | 
				
			||||||
 | 
							dev_err(&pdev->dev, "failed to register pincontrol\n");
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	/* enable and clear all interrupts */
 | 
						/* enable and clear all interrupts */
 | 
				
			||||||
	value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
 | 
						value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT |
 | 
				
			||||||
		DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
 | 
							DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT;
 | 
				
			||||||
| 
						 | 
					@ -586,7 +703,7 @@ int drm_dp_aux_enable(struct drm_dp_aux *aux)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tegra_dpaux *dpaux = to_dpaux(aux);
 | 
						struct tegra_dpaux *dpaux = to_dpaux(aux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return tegra_dpaux_pad_config(dpaux, DPAUX_HYBRID_PADCTL_MODE_AUX);
 | 
						return tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_AUX);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int drm_dp_aux_disable(struct drm_dp_aux *aux)
 | 
					int drm_dp_aux_disable(struct drm_dp_aux *aux)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue