diff options
author | Jan Safrata <jan.nikitenko@gmail.com> | 2014-09-26 04:40:29 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2014-12-01 22:42:49 -0500 |
commit | 5a52b1f2f65ae8f2b531b20504ebe21d6d8226f3 (patch) | |
tree | c934c9f47b9f02ee684bed4904d9e54d33f914d3 | |
parent | e8115e79aa62b6ebdb3e8e61ca4092cc32938afc (diff) |
drm/gma500: add support for atom e6xx lpc lvds i2c
add gpio bitbanging i2c adapter on LPC device of atom e6xx
gpu chipset to access lvds EDID
tested on SECO QuadMo747-E6xx-EXTREME Qseven platform
Reviewed-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
Signed-off-by: Jan Safrata <jan.nikitenko@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/gma500/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_lvds.c | 31 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/oaktrail_lvds_i2c.c | 170 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/psb_drv.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/psb_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/psb_intel_drv.h | 1 |
6 files changed, 214 insertions, 12 deletions
diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index b15315576376..190e55f2f891 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile | |||
@@ -39,6 +39,7 @@ gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \ | |||
39 | gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ | 39 | gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ |
40 | oaktrail_crtc.o \ | 40 | oaktrail_crtc.o \ |
41 | oaktrail_lvds.o \ | 41 | oaktrail_lvds.o \ |
42 | oaktrail_lvds_i2c.o \ | ||
42 | oaktrail_hdmi.o \ | 43 | oaktrail_hdmi.o \ |
43 | oaktrail_hdmi_i2c.o | 44 | oaktrail_hdmi_i2c.o |
44 | 45 | ||
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 0d39da6e8b7a..83bbc271bcfb 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c | |||
@@ -359,22 +359,26 @@ void oaktrail_lvds_init(struct drm_device *dev, | |||
359 | * if closed, act like it's not there for now | 359 | * if closed, act like it's not there for now |
360 | */ | 360 | */ |
361 | 361 | ||
362 | edid = NULL; | ||
362 | mutex_lock(&dev->mode_config.mutex); | 363 | mutex_lock(&dev->mode_config.mutex); |
363 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); | 364 | i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); |
364 | if (i2c_adap == NULL) | 365 | if (i2c_adap) |
365 | dev_err(dev->dev, "No ddc adapter available!\n"); | 366 | edid = drm_get_edid(connector, i2c_adap); |
367 | if (edid == NULL && dev_priv->lpc_gpio_base) { | ||
368 | oaktrail_lvds_i2c_init(encoder); | ||
369 | if (gma_encoder->ddc_bus != NULL) { | ||
370 | i2c_adap = &gma_encoder->ddc_bus->adapter; | ||
371 | edid = drm_get_edid(connector, i2c_adap); | ||
372 | } | ||
373 | } | ||
366 | /* | 374 | /* |
367 | * Attempt to get the fixed panel mode from DDC. Assume that the | 375 | * Attempt to get the fixed panel mode from DDC. Assume that the |
368 | * preferred mode is the right one. | 376 | * preferred mode is the right one. |
369 | */ | 377 | */ |
370 | if (i2c_adap) { | 378 | if (edid) { |
371 | edid = drm_get_edid(connector, i2c_adap); | 379 | drm_mode_connector_update_edid_property(connector, edid); |
372 | if (edid) { | 380 | drm_add_edid_modes(connector, edid); |
373 | drm_mode_connector_update_edid_property(connector, | 381 | kfree(edid); |
374 | edid); | ||
375 | drm_add_edid_modes(connector, edid); | ||
376 | kfree(edid); | ||
377 | } | ||
378 | 382 | ||
379 | list_for_each_entry(scan, &connector->probed_modes, head) { | 383 | list_for_each_entry(scan, &connector->probed_modes, head) { |
380 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | 384 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { |
@@ -383,7 +387,8 @@ void oaktrail_lvds_init(struct drm_device *dev, | |||
383 | goto out; /* FIXME: check for quirks */ | 387 | goto out; /* FIXME: check for quirks */ |
384 | } | 388 | } |
385 | } | 389 | } |
386 | } | 390 | } else |
391 | dev_err(dev->dev, "No ddc adapter available!\n"); | ||
387 | /* | 392 | /* |
388 | * If we didn't get EDID, try geting panel timing | 393 | * If we didn't get EDID, try geting panel timing |
389 | * from configuration data | 394 | * from configuration data |
@@ -411,8 +416,10 @@ failed_find: | |||
411 | mutex_unlock(&dev->mode_config.mutex); | 416 | mutex_unlock(&dev->mode_config.mutex); |
412 | 417 | ||
413 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); | 418 | dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); |
414 | if (gma_encoder->ddc_bus) | 419 | if (gma_encoder->ddc_bus) { |
415 | psb_intel_i2c_destroy(gma_encoder->ddc_bus); | 420 | psb_intel_i2c_destroy(gma_encoder->ddc_bus); |
421 | gma_encoder->ddc_bus = NULL; | ||
422 | } | ||
416 | 423 | ||
417 | /* failed_ddc: */ | 424 | /* failed_ddc: */ |
418 | 425 | ||
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 | } | ||
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 6ec3a905fdd2..92e7e5795398 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c | |||
@@ -212,6 +212,8 @@ static int psb_driver_unload(struct drm_device *dev) | |||
212 | } | 212 | } |
213 | if (dev_priv->aux_pdev) | 213 | if (dev_priv->aux_pdev) |
214 | pci_dev_put(dev_priv->aux_pdev); | 214 | pci_dev_put(dev_priv->aux_pdev); |
215 | if (dev_priv->lpc_pdev) | ||
216 | pci_dev_put(dev_priv->lpc_pdev); | ||
215 | 217 | ||
216 | /* Destroy VBT data */ | 218 | /* Destroy VBT data */ |
217 | psb_intel_destroy_bios(dev); | 219 | psb_intel_destroy_bios(dev); |
@@ -280,6 +282,24 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags) | |||
280 | DRM_DEBUG_KMS("Couldn't find aux pci device"); | 282 | DRM_DEBUG_KMS("Couldn't find aux pci device"); |
281 | } | 283 | } |
282 | dev_priv->gmbus_reg = dev_priv->aux_reg; | 284 | dev_priv->gmbus_reg = dev_priv->aux_reg; |
285 | |||
286 | dev_priv->lpc_pdev = pci_get_bus_and_slot(0, PCI_DEVFN(31, 0)); | ||
287 | if (dev_priv->lpc_pdev) { | ||
288 | pci_read_config_word(dev_priv->lpc_pdev, PSB_LPC_GBA, | ||
289 | &dev_priv->lpc_gpio_base); | ||
290 | pci_write_config_dword(dev_priv->lpc_pdev, PSB_LPC_GBA, | ||
291 | (u32)dev_priv->lpc_gpio_base | (1L<<31)); | ||
292 | pci_read_config_word(dev_priv->lpc_pdev, PSB_LPC_GBA, | ||
293 | &dev_priv->lpc_gpio_base); | ||
294 | dev_priv->lpc_gpio_base &= 0xffc0; | ||
295 | if (dev_priv->lpc_gpio_base) | ||
296 | DRM_DEBUG_KMS("Found LPC GPIO at 0x%04x\n", | ||
297 | dev_priv->lpc_gpio_base); | ||
298 | else { | ||
299 | pci_dev_put(dev_priv->lpc_pdev); | ||
300 | dev_priv->lpc_pdev = NULL; | ||
301 | } | ||
302 | } | ||
283 | } else { | 303 | } else { |
284 | dev_priv->gmbus_reg = dev_priv->vdc_reg; | 304 | dev_priv->gmbus_reg = dev_priv->vdc_reg; |
285 | } | 305 | } |
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 55ebe2bd88dd..e38057b91865 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h | |||
@@ -83,6 +83,7 @@ enum { | |||
83 | #define PSB_PGETBL_CTL 0x2020 | 83 | #define PSB_PGETBL_CTL 0x2020 |
84 | #define _PSB_PGETBL_ENABLED 0x00000001 | 84 | #define _PSB_PGETBL_ENABLED 0x00000001 |
85 | #define PSB_SGX_2D_SLAVE_PORT 0x4000 | 85 | #define PSB_SGX_2D_SLAVE_PORT 0x4000 |
86 | #define PSB_LPC_GBA 0x44 | ||
86 | 87 | ||
87 | /* TODO: To get rid of */ | 88 | /* TODO: To get rid of */ |
88 | #define PSB_TT_PRIV0_LIMIT (256*1024*1024) | 89 | #define PSB_TT_PRIV0_LIMIT (256*1024*1024) |
@@ -441,6 +442,7 @@ struct psb_ops; | |||
441 | struct drm_psb_private { | 442 | struct drm_psb_private { |
442 | struct drm_device *dev; | 443 | struct drm_device *dev; |
443 | struct pci_dev *aux_pdev; /* Currently only used by mrst */ | 444 | struct pci_dev *aux_pdev; /* Currently only used by mrst */ |
445 | struct pci_dev *lpc_pdev; /* Currently only used by mrst */ | ||
444 | const struct psb_ops *ops; | 446 | const struct psb_ops *ops; |
445 | const struct psb_offset *regmap; | 447 | const struct psb_offset *regmap; |
446 | 448 | ||
@@ -470,6 +472,7 @@ struct drm_psb_private { | |||
470 | uint8_t __iomem *sgx_reg; | 472 | uint8_t __iomem *sgx_reg; |
471 | uint8_t __iomem *vdc_reg; | 473 | uint8_t __iomem *vdc_reg; |
472 | uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */ | 474 | uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */ |
475 | uint16_t lpc_gpio_base; | ||
473 | uint32_t gatt_free_offset; | 476 | uint32_t gatt_free_offset; |
474 | 477 | ||
475 | /* Fencing / irq */ | 478 | /* Fencing / irq */ |
diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 336bd3aa1a06..860dd2177ca1 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h | |||
@@ -223,6 +223,7 @@ extern void oaktrail_lvds_init(struct drm_device *dev, | |||
223 | extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev); | 223 | extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev); |
224 | extern void oaktrail_dsi_init(struct drm_device *dev, | 224 | extern void oaktrail_dsi_init(struct drm_device *dev, |
225 | struct psb_intel_mode_device *mode_dev); | 225 | struct psb_intel_mode_device *mode_dev); |
226 | extern void oaktrail_lvds_i2c_init(struct drm_encoder *encoder); | ||
226 | extern void mid_dsi_init(struct drm_device *dev, | 227 | extern void mid_dsi_init(struct drm_device *dev, |
227 | struct psb_intel_mode_device *mode_dev, int dsi_num); | 228 | struct psb_intel_mode_device *mode_dev, int dsi_num); |
228 | 229 | ||