summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/l3.c
blob: b84f6f1f6800d8582ee312f597f1c1c1f7bf845b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: GPL-2.0-only
/*
 * L3 code
 *
 *  Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
 *
 * based on:
 *
 * L3 bus algorithm module.
 *
 *  Copyright (C) 2001 Russell King, All Rights Reserved.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>

#include <sound/l3.h>

/*
 * Send one byte of data to the chip.  Data is latched into the chip on
 * the rising edge of the clock.
 */
static void sendbyte(struct l3_pins *adap, unsigned int byte)
{
	int i;

	for (i = 0; i < 8; i++) {
		adap->setclk(adap, 0);
		udelay(adap->data_hold);
		adap->setdat(adap, byte & 1);
		udelay(adap->data_setup);
		adap->setclk(adap, 1);
		udelay(adap->clock_high);
		byte >>= 1;
	}
}

/*
 * Send a set of bytes to the chip.  We need to pulse the MODE line
 * between each byte, but never at the start nor at the end of the
 * transfer.
 */
static void sendbytes(struct l3_pins *adap, const u8 *buf,
		      int len)
{
	int i;

	for (i = 0; i < len; i++) {
		if (i) {
			udelay(adap->mode_hold);
			adap->setmode(adap, 0);
			udelay(adap->mode);
		}
		adap->setmode(adap, 1);
		udelay(adap->mode_setup);
		sendbyte(adap, buf[i]);
	}
}

int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
{
	adap->setclk(adap, 1);
	adap->setdat(adap, 1);
	adap->setmode(adap, 1);
	udelay(adap->mode);

	adap->setmode(adap, 0);
	udelay(adap->mode_setup);
	sendbyte(adap, addr);
	udelay(adap->mode_hold);

	sendbytes(adap, data, len);

	adap->setclk(adap, 1);
	adap->setdat(adap, 1);
	adap->setmode(adap, 0);

	return len;
}
EXPORT_SYMBOL_GPL(l3_write);


static void l3_set_clk(struct l3_pins *adap, int val)
{
	gpio_set_value(adap->gpio_clk, val);
}

static void l3_set_data(struct l3_pins *adap, int val)
{
	gpio_set_value(adap->gpio_data, val);
}

static void l3_set_mode(struct l3_pins *adap, int val)
{
	gpio_set_value(adap->gpio_mode, val);
}

int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap)
{
	int ret;

	if (!adap->use_gpios)
		return -EINVAL;

	ret = devm_gpio_request_one(dev, adap->gpio_data,
				GPIOF_OUT_INIT_LOW, "l3_data");
	if (ret < 0)
		return ret;
	adap->setdat = l3_set_data;

	ret = devm_gpio_request_one(dev, adap->gpio_clk,
				GPIOF_OUT_INIT_LOW, "l3_clk");
	if (ret < 0)
		return ret;
	adap->setclk = l3_set_clk;

	ret = devm_gpio_request_one(dev, adap->gpio_mode,
				GPIOF_OUT_INIT_LOW, "l3_mode");
	if (ret < 0)
		return ret;
	adap->setmode = l3_set_mode;

	return 0;
}
EXPORT_SYMBOL_GPL(l3_set_gpio_ops);

MODULE_DESCRIPTION("L3 bit-banging driver");
MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
MODULE_LICENSE("GPL");