diff options
author | Ondrej Zary <linux@rainbow-software.org> | 2011-04-18 06:14:57 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-04-19 02:50:53 -0400 |
commit | 86c0f043a737dadf034a4e6f29aefb074f4a1146 (patch) | |
tree | 4cacb1364fd037a0128b91e8f714335d2332851e /drivers/video | |
parent | 6e4b26805a90b6682dd4ea0fd426dcda8ca5164a (diff) |
s3fb: add DDC support
Add I2C support for the DDC bus and also default mode initialization by
reading monitor EDID to the s3fb driver.
Tested on Trio64V+ (2 cards), Trio64V2/DX, Virge (3 cards),
Virge/DX (3 cards), Virge/GX2, Trio3D/2X (4 cards), Trio3D.
Will probably not work on Trio32 - my 2 cards have DDC support in BIOS that
looks different from the other cards but the DDC pins on the VGA connector
are not connected.
Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 8 | ||||
-rw-r--r-- | drivers/video/s3fb.c | 206 |
2 files changed, 209 insertions, 5 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e6a8d8c0101d..e2126b5af1de 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1463,6 +1463,14 @@ config FB_S3 | |||
1463 | ---help--- | 1463 | ---help--- |
1464 | Driver for graphics boards with S3 Trio / S3 Virge chip. | 1464 | Driver for graphics boards with S3 Trio / S3 Virge chip. |
1465 | 1465 | ||
1466 | config FB_S3_DDC | ||
1467 | bool "DDC for S3 support" | ||
1468 | depends on FB_S3 | ||
1469 | select FB_DDC | ||
1470 | default y | ||
1471 | help | ||
1472 | Say Y here if you want DDC support for your S3 graphics card. | ||
1473 | |||
1466 | config FB_SAVAGE | 1474 | config FB_SAVAGE |
1467 | tristate "S3 Savage support" | 1475 | tristate "S3 Savage support" |
1468 | depends on FB && PCI && EXPERIMENTAL | 1476 | depends on FB && PCI && EXPERIMENTAL |
diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index c4482f2e5799..9a345209535b 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ | 25 | #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ |
26 | #include <video/vga.h> | 26 | #include <video/vga.h> |
27 | 27 | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/i2c-algo-bit.h> | ||
30 | |||
28 | #ifdef CONFIG_MTRR | 31 | #ifdef CONFIG_MTRR |
29 | #include <asm/mtrr.h> | 32 | #include <asm/mtrr.h> |
30 | #endif | 33 | #endif |
@@ -36,6 +39,12 @@ struct s3fb_info { | |||
36 | struct mutex open_lock; | 39 | struct mutex open_lock; |
37 | unsigned int ref_count; | 40 | unsigned int ref_count; |
38 | u32 pseudo_palette[16]; | 41 | u32 pseudo_palette[16]; |
42 | #ifdef CONFIG_FB_S3_DDC | ||
43 | u8 __iomem *mmio; | ||
44 | bool ddc_registered; | ||
45 | struct i2c_adapter ddc_adapter; | ||
46 | struct i2c_algo_bit_data ddc_algo; | ||
47 | #endif | ||
39 | }; | 48 | }; |
40 | 49 | ||
41 | 50 | ||
@@ -105,6 +114,9 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", | |||
105 | #define CHIP_UNDECIDED_FLAG 0x80 | 114 | #define CHIP_UNDECIDED_FLAG 0x80 |
106 | #define CHIP_MASK 0xFF | 115 | #define CHIP_MASK 0xFF |
107 | 116 | ||
117 | #define MMIO_OFFSET 0x1000000 | ||
118 | #define MMIO_SIZE 0x10000 | ||
119 | |||
108 | /* CRT timing register sets */ | 120 | /* CRT timing register sets */ |
109 | 121 | ||
110 | static const struct vga_regset s3_h_total_regs[] = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END}; | 122 | static const struct vga_regset s3_h_total_regs[] = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END}; |
@@ -140,7 +152,7 @@ static const struct svga_timing_regs s3_timing_regs = { | |||
140 | /* Module parameters */ | 152 | /* Module parameters */ |
141 | 153 | ||
142 | 154 | ||
143 | static char *mode_option __devinitdata = "640x480-8@60"; | 155 | static char *mode_option __devinitdata; |
144 | 156 | ||
145 | #ifdef CONFIG_MTRR | 157 | #ifdef CONFIG_MTRR |
146 | static int mtrr __devinitdata = 1; | 158 | static int mtrr __devinitdata = 1; |
@@ -169,6 +181,119 @@ MODULE_PARM_DESC(fasttext, "Enable S3 fast text mode (1=enable, 0=disable, defau | |||
169 | 181 | ||
170 | /* ------------------------------------------------------------------------- */ | 182 | /* ------------------------------------------------------------------------- */ |
171 | 183 | ||
184 | #ifdef CONFIG_FB_S3_DDC | ||
185 | |||
186 | #define DDC_REG 0xaa /* Trio 3D/1X/2X */ | ||
187 | #define DDC_MMIO_REG 0xff20 /* all other chips */ | ||
188 | #define DDC_SCL_OUT (1 << 0) | ||
189 | #define DDC_SDA_OUT (1 << 1) | ||
190 | #define DDC_SCL_IN (1 << 2) | ||
191 | #define DDC_SDA_IN (1 << 3) | ||
192 | #define DDC_DRIVE_EN (1 << 4) | ||
193 | |||
194 | static bool s3fb_ddc_needs_mmio(int chip) | ||
195 | { | ||
196 | return !(chip == CHIP_360_TRIO3D_1X || | ||
197 | chip == CHIP_362_TRIO3D_2X || | ||
198 | chip == CHIP_368_TRIO3D_2X); | ||
199 | } | ||
200 | |||
201 | static u8 s3fb_ddc_read(struct s3fb_info *par) | ||
202 | { | ||
203 | if (s3fb_ddc_needs_mmio(par->chip)) | ||
204 | return readb(par->mmio + DDC_MMIO_REG); | ||
205 | else | ||
206 | return vga_rcrt(par->state.vgabase, DDC_REG); | ||
207 | } | ||
208 | |||
209 | static void s3fb_ddc_write(struct s3fb_info *par, u8 val) | ||
210 | { | ||
211 | if (s3fb_ddc_needs_mmio(par->chip)) | ||
212 | writeb(val, par->mmio + DDC_MMIO_REG); | ||
213 | else | ||
214 | vga_wcrt(par->state.vgabase, DDC_REG, val); | ||
215 | } | ||
216 | |||
217 | static void s3fb_ddc_setscl(void *data, int val) | ||
218 | { | ||
219 | struct s3fb_info *par = data; | ||
220 | unsigned char reg; | ||
221 | |||
222 | reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; | ||
223 | if (val) | ||
224 | reg |= DDC_SCL_OUT; | ||
225 | else | ||
226 | reg &= ~DDC_SCL_OUT; | ||
227 | s3fb_ddc_write(par, reg); | ||
228 | } | ||
229 | |||
230 | static void s3fb_ddc_setsda(void *data, int val) | ||
231 | { | ||
232 | struct s3fb_info *par = data; | ||
233 | unsigned char reg; | ||
234 | |||
235 | reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; | ||
236 | if (val) | ||
237 | reg |= DDC_SDA_OUT; | ||
238 | else | ||
239 | reg &= ~DDC_SDA_OUT; | ||
240 | s3fb_ddc_write(par, reg); | ||
241 | } | ||
242 | |||
243 | static int s3fb_ddc_getscl(void *data) | ||
244 | { | ||
245 | struct s3fb_info *par = data; | ||
246 | |||
247 | return !!(s3fb_ddc_read(par) & DDC_SCL_IN); | ||
248 | } | ||
249 | |||
250 | static int s3fb_ddc_getsda(void *data) | ||
251 | { | ||
252 | struct s3fb_info *par = data; | ||
253 | |||
254 | return !!(s3fb_ddc_read(par) & DDC_SDA_IN); | ||
255 | } | ||
256 | |||
257 | static int __devinit s3fb_setup_ddc_bus(struct fb_info *info) | ||
258 | { | ||
259 | struct s3fb_info *par = info->par; | ||
260 | |||
261 | strlcpy(par->ddc_adapter.name, info->fix.id, | ||
262 | sizeof(par->ddc_adapter.name)); | ||
263 | par->ddc_adapter.owner = THIS_MODULE; | ||
264 | par->ddc_adapter.class = I2C_CLASS_DDC; | ||
265 | par->ddc_adapter.algo_data = &par->ddc_algo; | ||
266 | par->ddc_adapter.dev.parent = info->device; | ||
267 | par->ddc_algo.setsda = s3fb_ddc_setsda; | ||
268 | par->ddc_algo.setscl = s3fb_ddc_setscl; | ||
269 | par->ddc_algo.getsda = s3fb_ddc_getsda; | ||
270 | par->ddc_algo.getscl = s3fb_ddc_getscl; | ||
271 | par->ddc_algo.udelay = 10; | ||
272 | par->ddc_algo.timeout = 20; | ||
273 | par->ddc_algo.data = par; | ||
274 | |||
275 | i2c_set_adapdata(&par->ddc_adapter, par); | ||
276 | |||
277 | /* | ||
278 | * some Virge cards have external MUX to switch chip I2C bus between | ||
279 | * DDC and extension pins - switch it do DDC | ||
280 | */ | ||
281 | /* vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */ | ||
282 | if (par->chip == CHIP_357_VIRGE_GX2 || | ||
283 | par->chip == CHIP_359_VIRGE_GX2P) | ||
284 | svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03); | ||
285 | else | ||
286 | svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03); | ||
287 | /* some Virge need this or the DDC is ignored */ | ||
288 | svga_wcrt_mask(par->state.vgabase, 0x5c, 0x03, 0x03); | ||
289 | |||
290 | return i2c_bit_add_bus(&par->ddc_adapter); | ||
291 | } | ||
292 | #endif /* CONFIG_FB_S3_DDC */ | ||
293 | |||
294 | |||
295 | /* ------------------------------------------------------------------------- */ | ||
296 | |||
172 | /* Set font in S3 fast text mode */ | 297 | /* Set font in S3 fast text mode */ |
173 | 298 | ||
174 | static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map) | 299 | static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map) |
@@ -994,6 +1119,7 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i | |||
994 | struct s3fb_info *par; | 1119 | struct s3fb_info *par; |
995 | int rc; | 1120 | int rc; |
996 | u8 regval, cr38, cr39; | 1121 | u8 regval, cr38, cr39; |
1122 | bool found = false; | ||
997 | 1123 | ||
998 | /* Ignore secondary VGA device because there is no VGA arbitration */ | 1124 | /* Ignore secondary VGA device because there is no VGA arbitration */ |
999 | if (! svga_primary_device(dev)) { | 1125 | if (! svga_primary_device(dev)) { |
@@ -1110,12 +1236,69 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i | |||
1110 | info->fix.ypanstep = 0; | 1236 | info->fix.ypanstep = 0; |
1111 | info->fix.accel = FB_ACCEL_NONE; | 1237 | info->fix.accel = FB_ACCEL_NONE; |
1112 | info->pseudo_palette = (void*) (par->pseudo_palette); | 1238 | info->pseudo_palette = (void*) (par->pseudo_palette); |
1239 | info->var.bits_per_pixel = 8; | ||
1240 | |||
1241 | #ifdef CONFIG_FB_S3_DDC | ||
1242 | /* Enable MMIO if needed */ | ||
1243 | if (s3fb_ddc_needs_mmio(par->chip)) { | ||
1244 | par->mmio = ioremap(info->fix.smem_start + MMIO_OFFSET, MMIO_SIZE); | ||
1245 | if (par->mmio) | ||
1246 | svga_wcrt_mask(par->state.vgabase, 0x53, 0x08, 0x08); /* enable MMIO */ | ||
1247 | else | ||
1248 | dev_err(info->device, "unable to map MMIO at 0x%lx, disabling DDC", | ||
1249 | info->fix.smem_start + MMIO_OFFSET); | ||
1250 | } | ||
1251 | if (!s3fb_ddc_needs_mmio(par->chip) || par->mmio) | ||
1252 | if (s3fb_setup_ddc_bus(info) == 0) { | ||
1253 | u8 *edid = fb_ddc_read(&par->ddc_adapter); | ||
1254 | par->ddc_registered = true; | ||
1255 | if (edid) { | ||
1256 | fb_edid_to_monspecs(edid, &info->monspecs); | ||
1257 | kfree(edid); | ||
1258 | if (!info->monspecs.modedb) | ||
1259 | dev_err(info->device, "error getting mode database\n"); | ||
1260 | else { | ||
1261 | const struct fb_videomode *m; | ||
1262 | |||
1263 | fb_videomode_to_modelist(info->monspecs.modedb, | ||
1264 | info->monspecs.modedb_len, | ||
1265 | &info->modelist); | ||
1266 | m = fb_find_best_display(&info->monspecs, &info->modelist); | ||
1267 | if (m) { | ||
1268 | fb_videomode_to_var(&info->var, m); | ||
1269 | /* fill all other info->var's fields */ | ||
1270 | if (s3fb_check_var(&info->var, info) == 0) | ||
1271 | found = true; | ||
1272 | } | ||
1273 | } | ||
1274 | } | ||
1275 | } | ||
1276 | #endif | ||
1277 | if (!mode_option && !found) | ||
1278 | mode_option = "640x480-8@60"; | ||
1113 | 1279 | ||
1114 | /* Prepare startup mode */ | 1280 | /* Prepare startup mode */ |
1115 | rc = fb_find_mode(&(info->var), info, mode_option, NULL, 0, NULL, 8); | 1281 | if (mode_option) { |
1116 | if (! ((rc == 1) || (rc == 2))) { | 1282 | rc = fb_find_mode(&info->var, info, mode_option, |
1117 | rc = -EINVAL; | 1283 | info->monspecs.modedb, info->monspecs.modedb_len, |
1118 | dev_err(info->device, "mode %s not found\n", mode_option); | 1284 | NULL, info->var.bits_per_pixel); |
1285 | if (!rc || rc == 4) { | ||
1286 | rc = -EINVAL; | ||
1287 | dev_err(info->device, "mode %s not found\n", mode_option); | ||
1288 | fb_destroy_modedb(info->monspecs.modedb); | ||
1289 | info->monspecs.modedb = NULL; | ||
1290 | goto err_find_mode; | ||
1291 | } | ||
1292 | } | ||
1293 | |||
1294 | fb_destroy_modedb(info->monspecs.modedb); | ||
1295 | info->monspecs.modedb = NULL; | ||
1296 | |||
1297 | /* maximize virtual vertical size for fast scrolling */ | ||
1298 | info->var.yres_virtual = info->fix.smem_len * 8 / | ||
1299 | (info->var.bits_per_pixel * info->var.xres_virtual); | ||
1300 | if (info->var.yres_virtual < info->var.yres) { | ||
1301 | dev_err(info->device, "virtual vertical size smaller than real\n"); | ||
1119 | goto err_find_mode; | 1302 | goto err_find_mode; |
1120 | } | 1303 | } |
1121 | 1304 | ||
@@ -1164,6 +1347,12 @@ err_reg_fb: | |||
1164 | fb_dealloc_cmap(&info->cmap); | 1347 | fb_dealloc_cmap(&info->cmap); |
1165 | err_alloc_cmap: | 1348 | err_alloc_cmap: |
1166 | err_find_mode: | 1349 | err_find_mode: |
1350 | #ifdef CONFIG_FB_S3_DDC | ||
1351 | if (par->ddc_registered) | ||
1352 | i2c_del_adapter(&par->ddc_adapter); | ||
1353 | if (par->mmio) | ||
1354 | iounmap(par->mmio); | ||
1355 | #endif | ||
1167 | pci_iounmap(dev, info->screen_base); | 1356 | pci_iounmap(dev, info->screen_base); |
1168 | err_iomap: | 1357 | err_iomap: |
1169 | pci_release_regions(dev); | 1358 | pci_release_regions(dev); |
@@ -1195,6 +1384,13 @@ static void __devexit s3_pci_remove(struct pci_dev *dev) | |||
1195 | unregister_framebuffer(info); | 1384 | unregister_framebuffer(info); |
1196 | fb_dealloc_cmap(&info->cmap); | 1385 | fb_dealloc_cmap(&info->cmap); |
1197 | 1386 | ||
1387 | #ifdef CONFIG_FB_S3_DDC | ||
1388 | if (par->ddc_registered) | ||
1389 | i2c_del_adapter(&par->ddc_adapter); | ||
1390 | if (par->mmio) | ||
1391 | iounmap(par->mmio); | ||
1392 | #endif | ||
1393 | |||
1198 | pci_iounmap(dev, info->screen_base); | 1394 | pci_iounmap(dev, info->screen_base); |
1199 | pci_release_regions(dev); | 1395 | pci_release_regions(dev); |
1200 | /* pci_disable_device(dev); */ | 1396 | /* pci_disable_device(dev); */ |