diff options
Diffstat (limited to 'drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c')
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c new file mode 100644 index 000000000000..f913a62eee5f --- /dev/null +++ b/drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2002-2010, Intel Corporation. | ||
3 | * Copyright (c) 2014 ATRON electronic GmbH | ||
4 | * Author: Jan Safrata <jan.nikitenko@gmail.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
7 | * of this software and associated documentation files (the "Software"), to deal | ||
8 | * in the Software without restriction, including without limitation the rights | ||
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
10 | * copies of the Software, and to permit persons to whom the Software is | ||
11 | * furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice shall be included in | ||
14 | * all copies or substantial portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | * THE SOFTWARE. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/i2c-algo-bit.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/io.h> | ||
34 | #include <linux/delay.h> | ||
35 | |||
36 | #include <drm/drmP.h> | ||
37 | #include "psb_drv.h" | ||
38 | #include "psb_intel_reg.h" | ||
39 | |||
40 | |||
41 | /* | ||
42 | * LPC GPIO based I2C bus for LVDS of Atom E6xx | ||
43 | */ | ||
44 | |||
45 | /*----------------------------------------------------------------------------- | ||
46 | * LPC Register Offsets. Used for LVDS GPIO Bit Bashing. Registers are part | ||
47 | * Atom E6xx [D31:F0] | ||
48 | ----------------------------------------------------------------------------*/ | ||
49 | #define RGEN 0x20 | ||
50 | #define RGIO 0x24 | ||
51 | #define RGLVL 0x28 | ||
52 | #define RGTPE 0x2C | ||
53 | #define RGTNE 0x30 | ||
54 | #define RGGPE 0x34 | ||
55 | #define RGSMI 0x38 | ||
56 | #define RGTS 0x3C | ||
57 | |||
58 | /* The LVDS GPIO clock lines are GPIOSUS[3] | ||
59 | * The LVDS GPIO data lines are GPIOSUS[4] | ||
60 | */ | ||
61 | #define GPIO_CLOCK 0x08 | ||
62 | #define GPIO_DATA 0x10 | ||
63 | |||
64 | #define LPC_READ_REG(chan, r) inl((chan)->reg + (r)) | ||
65 | #define LPC_WRITE_REG(chan, r, val) outl((val), (chan)->reg + (r)) | ||
66 | |||
67 | static int get_clock(void *data) | ||
68 | { | ||
69 | struct psb_intel_i2c_chan *chan = data; | ||
70 | u32 val, tmp; | ||
71 | |||
72 | val = LPC_READ_REG(chan, RGIO); | ||
73 | val |= GPIO_CLOCK; | ||
74 | LPC_WRITE_REG(chan, RGIO, val); | ||
75 | tmp = LPC_READ_REG(chan, RGLVL); | ||
76 | val = (LPC_READ_REG(chan, RGLVL) & GPIO_CLOCK) ? 1 : 0; | ||
77 | |||
78 | return val; | ||
79 | } | ||
80 | |||
81 | static int get_data(void *data) | ||
82 | { | ||
83 | struct psb_intel_i2c_chan *chan = data; | ||
84 | u32 val, tmp; | ||
85 | |||
86 | val = LPC_READ_REG(chan, RGIO); | ||
87 | val |= GPIO_DATA; | ||
88 | LPC_WRITE_REG(chan, RGIO, val); | ||
89 | tmp = LPC_READ_REG(chan, RGLVL); | ||
90 | val = (LPC_READ_REG(chan, RGLVL) & GPIO_DATA) ? 1 : 0; | ||
91 | |||
92 | return val; | ||
93 | } | ||
94 | |||
95 | static void set_clock(void *data, int state_high) | ||
96 | { | ||
97 | struct psb_intel_i2c_chan *chan = data; | ||
98 | u32 val; | ||
99 | |||
100 | if (state_high) { | ||
101 | val = LPC_READ_REG(chan, RGIO); | ||
102 | val |= GPIO_CLOCK; | ||
103 | LPC_WRITE_REG(chan, RGIO, val); | ||
104 | } else { | ||
105 | val = LPC_READ_REG(chan, RGIO); | ||
106 | val &= ~GPIO_CLOCK; | ||
107 | LPC_WRITE_REG(chan, RGIO, val); | ||
108 | val = LPC_READ_REG(chan, RGLVL); | ||
109 | val &= ~GPIO_CLOCK; | ||
110 | LPC_WRITE_REG(chan, RGLVL, val); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static void set_data(void *data, int state_high) | ||
115 | { | ||
116 | struct psb_intel_i2c_chan *chan = data; | ||
117 | u32 val; | ||
118 | |||
119 | if (state_high) { | ||
120 | val = LPC_READ_REG(chan, RGIO); | ||
121 | val |= GPIO_DATA; | ||
122 | LPC_WRITE_REG(chan, RGIO, val); | ||
123 | } else { | ||
124 | val = LPC_READ_REG(chan, RGIO); | ||
125 | val &= ~GPIO_DATA; | ||
126 | LPC_WRITE_REG(chan, RGIO, val); | ||
127 | val = LPC_READ_REG(chan, RGLVL); | ||
128 | val &= ~GPIO_DATA; | ||
129 | LPC_WRITE_REG(chan, RGLVL, val); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | void oaktrail_lvds_i2c_init(struct drm_encoder *encoder) | ||
134 | { | ||
135 | struct drm_device *dev = encoder->dev; | ||
136 | struct gma_encoder *gma_encoder = to_gma_encoder(encoder); | ||
137 | struct drm_psb_private *dev_priv = dev->dev_private; | ||
138 | struct psb_intel_i2c_chan *chan; | ||
139 | |||
140 | chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); | ||
141 | if (!chan) | ||
142 | return; | ||
143 | |||
144 | chan->drm_dev = dev; | ||
145 | chan->reg = dev_priv->lpc_gpio_base; | ||
146 | strncpy(chan->adapter.name, "gma500 LPC", I2C_NAME_SIZE - 1); | ||
147 | chan->adapter.owner = THIS_MODULE; | ||
148 | chan->adapter.algo_data = &chan->algo; | ||
149 | chan->adapter.dev.parent = &dev->pdev->dev; | ||
150 | chan->algo.setsda = set_data; | ||
151 | chan->algo.setscl = set_clock; | ||
152 | chan->algo.getsda = get_data; | ||
153 | chan->algo.getscl = get_clock; | ||
154 | chan->algo.udelay = 100; | ||
155 | chan->algo.timeout = usecs_to_jiffies(2200); | ||
156 | chan->algo.data = chan; | ||
157 | |||
158 | i2c_set_adapdata(&chan->adapter, chan); | ||
159 | |||
160 | set_data(chan, 1); | ||
161 | set_clock(chan, 1); | ||
162 | udelay(50); | ||
163 | |||
164 | if (i2c_bit_add_bus(&chan->adapter)) { | ||
165 | kfree(chan); | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | gma_encoder->ddc_bus = chan; | ||
170 | } | ||