drivers/input/touchscreen/imx6ul_tsc.c

Source file repositories/reference/linux-study-clean/drivers/input/touchscreen/imx6ul_tsc.c

File Facts

System
Linux kernel
Corpus path
drivers/input/touchscreen/imx6ul_tsc.c
Extension
.c
Size
14979 bytes
Lines
602
Domain
Driver Families
Bucket
drivers/input
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct imx6ul_tsc {
	struct device *dev;
	struct input_dev *input;
	void __iomem *tsc_regs;
	void __iomem *adc_regs;
	struct clk *tsc_clk;
	struct clk *adc_clk;
	struct gpio_desc *xnur_gpio;

	u32 measure_delay_time;
	u32 pre_charge_time;
	bool average_enable;
	u32 average_select;
	u32 de_glitch;

	struct completion completion;
};

/*
 * TSC module need ADC to get the measure value. So
 * before config TSC, we should initialize ADC module.
 */
static int imx6ul_adc_init(struct imx6ul_tsc *tsc)
{
	u32 adc_hc = 0;
	u32 adc_gc;
	u32 adc_gs;
	u32 adc_cfg;
	unsigned long timeout;

	reinit_completion(&tsc->completion);

	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
	adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK);
	adc_cfg |= FIELD_PREP(ADC_CONV_MODE_MASK, ADC_12BIT_MODE) |
		FIELD_PREP(ADC_INPUT_CLK_MASK, ADC_IPG_CLK);
	adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE);
	adc_cfg |= FIELD_PREP(ADC_CLK_DIV_MASK, ADC_CLK_DIV_8);
	if (tsc->average_enable) {
		adc_cfg &= ~ADC_AVGS_MASK;
		adc_cfg |= FIELD_PREP(ADC_AVGS_MASK, tsc->average_select);
	}
	adc_cfg &= ~ADC_HARDWARE_TRIGGER;
	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);

	/* enable calibration interrupt */
	adc_hc |= ADC_AIEN;
	adc_hc |= FIELD_PREP(ADC_ADCH_MASK, ADC_CONV_DISABLE);
	writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);

	/* start ADC calibration */
	adc_gc = readl(tsc->adc_regs + REG_ADC_GC);
	adc_gc |= ADC_CAL;
	if (tsc->average_enable)
		adc_gc |= ADC_AVGE;
	writel(adc_gc, tsc->adc_regs + REG_ADC_GC);

	timeout = wait_for_completion_timeout
			(&tsc->completion, ADC_TIMEOUT);
	if (timeout == 0) {
		dev_err(tsc->dev, "Timeout for adc calibration\n");
		return -ETIMEDOUT;
	}

	adc_gs = readl(tsc->adc_regs + REG_ADC_GS);
	if (adc_gs & ADC_CALF) {
		dev_err(tsc->dev, "ADC calibration failed\n");
		return -EINVAL;
	}

	/* TSC need the ADC work in hardware trigger */
	adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
	adc_cfg |= ADC_HARDWARE_TRIGGER;
	writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);

	return 0;
}

/*
 * This is a TSC workaround. Currently TSC misconnect two
 * ADC channels, this function remap channel configure for
 * hardware trigger.
 */
static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
{
	u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;

	adc_hc0 = FIELD_PREP(ADC_AIEN, 0);
	writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);

Annotation

Implementation Notes