xref: /openbmc/linux/drivers/gpu/drm/ast/ast_dp501.c (revision f81bb0ac)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
283c6620bSDave Airlie 
3fbbbd160SSam Ravnborg #include <linux/delay.h>
483c6620bSDave Airlie #include <linux/firmware.h>
5fbbbd160SSam Ravnborg #include <linux/module.h>
6fbbbd160SSam Ravnborg 
783c6620bSDave Airlie #include "ast_drv.h"
8fbbbd160SSam Ravnborg 
983c6620bSDave Airlie MODULE_FIRMWARE("ast_dp501_fw.bin");
1083c6620bSDave Airlie 
ast_release_firmware(void * data)112c0b6566SThomas Zimmermann static void ast_release_firmware(void *data)
122c0b6566SThomas Zimmermann {
1337b42cf9SThomas Zimmermann 	struct ast_device *ast = data;
142c0b6566SThomas Zimmermann 
152c0b6566SThomas Zimmermann 	release_firmware(ast->dp501_fw);
162c0b6566SThomas Zimmermann 	ast->dp501_fw = NULL;
172c0b6566SThomas Zimmermann }
182c0b6566SThomas Zimmermann 
ast_load_dp501_microcode(struct drm_device * dev)1912f8030eSEgbert Eich static int ast_load_dp501_microcode(struct drm_device *dev)
2083c6620bSDave Airlie {
215abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
222c0b6566SThomas Zimmermann 	int ret;
2383c6620bSDave Airlie 
242c0b6566SThomas Zimmermann 	ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
252c0b6566SThomas Zimmermann 	if (ret)
262c0b6566SThomas Zimmermann 		return ret;
272c0b6566SThomas Zimmermann 
282c0b6566SThomas Zimmermann 	return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast);
2983c6620bSDave Airlie }
3083c6620bSDave Airlie 
send_ack(struct ast_device * ast)3137b42cf9SThomas Zimmermann static void send_ack(struct ast_device *ast)
3283c6620bSDave Airlie {
3383c6620bSDave Airlie 	u8 sendack;
3483c6620bSDave Airlie 	sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
3583c6620bSDave Airlie 	sendack |= 0x80;
3683c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
3783c6620bSDave Airlie }
3883c6620bSDave Airlie 
send_nack(struct ast_device * ast)3937b42cf9SThomas Zimmermann static void send_nack(struct ast_device *ast)
4083c6620bSDave Airlie {
4183c6620bSDave Airlie 	u8 sendack;
4283c6620bSDave Airlie 	sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
4383c6620bSDave Airlie 	sendack &= ~0x80;
4483c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
4583c6620bSDave Airlie }
4683c6620bSDave Airlie 
wait_ack(struct ast_device * ast)4737b42cf9SThomas Zimmermann static bool wait_ack(struct ast_device *ast)
4883c6620bSDave Airlie {
4983c6620bSDave Airlie 	u8 waitack;
5083c6620bSDave Airlie 	u32 retry = 0;
5183c6620bSDave Airlie 	do {
5283c6620bSDave Airlie 		waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
5383c6620bSDave Airlie 		waitack &= 0x80;
5483c6620bSDave Airlie 		udelay(100);
5583c6620bSDave Airlie 	} while ((!waitack) && (retry++ < 1000));
5683c6620bSDave Airlie 
5783c6620bSDave Airlie 	if (retry < 1000)
5883c6620bSDave Airlie 		return true;
5983c6620bSDave Airlie 	else
6083c6620bSDave Airlie 		return false;
6183c6620bSDave Airlie }
6283c6620bSDave Airlie 
wait_nack(struct ast_device * ast)6337b42cf9SThomas Zimmermann static bool wait_nack(struct ast_device *ast)
6483c6620bSDave Airlie {
6583c6620bSDave Airlie 	u8 waitack;
6683c6620bSDave Airlie 	u32 retry = 0;
6783c6620bSDave Airlie 	do {
6883c6620bSDave Airlie 		waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
6983c6620bSDave Airlie 		waitack &= 0x80;
7083c6620bSDave Airlie 		udelay(100);
7183c6620bSDave Airlie 	} while ((waitack) && (retry++ < 1000));
7283c6620bSDave Airlie 
7383c6620bSDave Airlie 	if (retry < 1000)
7483c6620bSDave Airlie 		return true;
7583c6620bSDave Airlie 	else
7683c6620bSDave Airlie 		return false;
7783c6620bSDave Airlie }
7883c6620bSDave Airlie 
set_cmd_trigger(struct ast_device * ast)7937b42cf9SThomas Zimmermann static void set_cmd_trigger(struct ast_device *ast)
8083c6620bSDave Airlie {
8183c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40);
8283c6620bSDave Airlie }
8383c6620bSDave Airlie 
clear_cmd_trigger(struct ast_device * ast)8437b42cf9SThomas Zimmermann static void clear_cmd_trigger(struct ast_device *ast)
8583c6620bSDave Airlie {
8683c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00);
8783c6620bSDave Airlie }
8883c6620bSDave Airlie 
8983c6620bSDave Airlie #if 0
9037b42cf9SThomas Zimmermann static bool wait_fw_ready(struct ast_device *ast)
9183c6620bSDave Airlie {
9283c6620bSDave Airlie 	u8 waitready;
9383c6620bSDave Airlie 	u32 retry = 0;
9483c6620bSDave Airlie 	do {
9583c6620bSDave Airlie 		waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
9683c6620bSDave Airlie 		waitready &= 0x40;
9783c6620bSDave Airlie 		udelay(100);
9883c6620bSDave Airlie 	} while ((!waitready) && (retry++ < 1000));
9983c6620bSDave Airlie 
10083c6620bSDave Airlie 	if (retry < 1000)
10183c6620bSDave Airlie 		return true;
10283c6620bSDave Airlie 	else
10383c6620bSDave Airlie 		return false;
10483c6620bSDave Airlie }
10583c6620bSDave Airlie #endif
10683c6620bSDave Airlie 
ast_write_cmd(struct drm_device * dev,u8 data)10783c6620bSDave Airlie static bool ast_write_cmd(struct drm_device *dev, u8 data)
10883c6620bSDave Airlie {
1095abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
11083c6620bSDave Airlie 	int retry = 0;
11183c6620bSDave Airlie 	if (wait_nack(ast)) {
11283c6620bSDave Airlie 		send_nack(ast);
11383c6620bSDave Airlie 		ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
11483c6620bSDave Airlie 		send_ack(ast);
11583c6620bSDave Airlie 		set_cmd_trigger(ast);
11683c6620bSDave Airlie 		do {
11783c6620bSDave Airlie 			if (wait_ack(ast)) {
11883c6620bSDave Airlie 				clear_cmd_trigger(ast);
11983c6620bSDave Airlie 				send_nack(ast);
12083c6620bSDave Airlie 				return true;
12183c6620bSDave Airlie 			}
12283c6620bSDave Airlie 		} while (retry++ < 100);
12383c6620bSDave Airlie 	}
12483c6620bSDave Airlie 	clear_cmd_trigger(ast);
12583c6620bSDave Airlie 	send_nack(ast);
12683c6620bSDave Airlie 	return false;
12783c6620bSDave Airlie }
12883c6620bSDave Airlie 
ast_write_data(struct drm_device * dev,u8 data)12983c6620bSDave Airlie static bool ast_write_data(struct drm_device *dev, u8 data)
13083c6620bSDave Airlie {
1315abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
13283c6620bSDave Airlie 
13383c6620bSDave Airlie 	if (wait_nack(ast)) {
13483c6620bSDave Airlie 		send_nack(ast);
13583c6620bSDave Airlie 		ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
13683c6620bSDave Airlie 		send_ack(ast);
13783c6620bSDave Airlie 		if (wait_ack(ast)) {
13883c6620bSDave Airlie 			send_nack(ast);
13983c6620bSDave Airlie 			return true;
14083c6620bSDave Airlie 		}
14183c6620bSDave Airlie 	}
14283c6620bSDave Airlie 	send_nack(ast);
14383c6620bSDave Airlie 	return false;
14483c6620bSDave Airlie }
14583c6620bSDave Airlie 
14683c6620bSDave Airlie #if 0
14783c6620bSDave Airlie static bool ast_read_data(struct drm_device *dev, u8 *data)
14883c6620bSDave Airlie {
1495abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
15083c6620bSDave Airlie 	u8 tmp;
15183c6620bSDave Airlie 
15283c6620bSDave Airlie 	*data = 0;
15383c6620bSDave Airlie 
15483c6620bSDave Airlie 	if (wait_ack(ast) == false)
15583c6620bSDave Airlie 		return false;
15683c6620bSDave Airlie 	tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff);
15783c6620bSDave Airlie 	*data = tmp;
15883c6620bSDave Airlie 	if (wait_nack(ast) == false) {
15983c6620bSDave Airlie 		send_nack(ast);
16083c6620bSDave Airlie 		return false;
16183c6620bSDave Airlie 	}
16283c6620bSDave Airlie 	send_nack(ast);
16383c6620bSDave Airlie 	return true;
16483c6620bSDave Airlie }
16583c6620bSDave Airlie 
16637b42cf9SThomas Zimmermann static void clear_cmd(struct ast_device *ast)
16783c6620bSDave Airlie {
16883c6620bSDave Airlie 	send_nack(ast);
16983c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00);
17083c6620bSDave Airlie }
17183c6620bSDave Airlie #endif
17283c6620bSDave Airlie 
ast_set_dp501_video_output(struct drm_device * dev,u8 mode)17383c6620bSDave Airlie void ast_set_dp501_video_output(struct drm_device *dev, u8 mode)
17483c6620bSDave Airlie {
17583c6620bSDave Airlie 	ast_write_cmd(dev, 0x40);
17683c6620bSDave Airlie 	ast_write_data(dev, mode);
17783c6620bSDave Airlie 
17883c6620bSDave Airlie 	msleep(10);
17983c6620bSDave Airlie }
18083c6620bSDave Airlie 
get_fw_base(struct ast_device * ast)18137b42cf9SThomas Zimmermann static u32 get_fw_base(struct ast_device *ast)
18283c6620bSDave Airlie {
18383c6620bSDave Airlie 	return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
18483c6620bSDave Airlie }
18583c6620bSDave Airlie 
ast_backup_fw(struct drm_device * dev,u8 * addr,u32 size)18683c6620bSDave Airlie bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
18783c6620bSDave Airlie {
1885abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
18983c6620bSDave Airlie 	u32 i, data;
19083c6620bSDave Airlie 	u32 boot_address;
19183c6620bSDave Airlie 
192ba4e0339SKuoHsiang Chou 	if (ast->config_mode != ast_use_p2a)
193ba4e0339SKuoHsiang Chou 		return false;
194ba4e0339SKuoHsiang Chou 
19583c6620bSDave Airlie 	data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
19683c6620bSDave Airlie 	if (data) {
19783c6620bSDave Airlie 		boot_address = get_fw_base(ast);
19883c6620bSDave Airlie 		for (i = 0; i < size; i += 4)
19983c6620bSDave Airlie 			*(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i);
20083c6620bSDave Airlie 		return true;
20183c6620bSDave Airlie 	}
20283c6620bSDave Airlie 	return false;
20383c6620bSDave Airlie }
20483c6620bSDave Airlie 
ast_launch_m68k(struct drm_device * dev)20512f8030eSEgbert Eich static bool ast_launch_m68k(struct drm_device *dev)
20683c6620bSDave Airlie {
2075abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
20883c6620bSDave Airlie 	u32 i, data, len = 0;
20983c6620bSDave Airlie 	u32 boot_address;
21083c6620bSDave Airlie 	u8 *fw_addr = NULL;
21183c6620bSDave Airlie 	u8 jreg;
21283c6620bSDave Airlie 
213ba4e0339SKuoHsiang Chou 	if (ast->config_mode != ast_use_p2a)
214ba4e0339SKuoHsiang Chou 		return false;
215ba4e0339SKuoHsiang Chou 
21683c6620bSDave Airlie 	data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
21783c6620bSDave Airlie 	if (!data) {
21883c6620bSDave Airlie 
21983c6620bSDave Airlie 		if (ast->dp501_fw_addr) {
22083c6620bSDave Airlie 			fw_addr = ast->dp501_fw_addr;
22183c6620bSDave Airlie 			len = 32*1024;
22212f8030eSEgbert Eich 		} else {
22312f8030eSEgbert Eich 			if (!ast->dp501_fw &&
22412f8030eSEgbert Eich 			    ast_load_dp501_microcode(dev) < 0)
22512f8030eSEgbert Eich 				return false;
22612f8030eSEgbert Eich 
22783c6620bSDave Airlie 			fw_addr = (u8 *)ast->dp501_fw->data;
22883c6620bSDave Airlie 			len = ast->dp501_fw->size;
22983c6620bSDave Airlie 		}
23083c6620bSDave Airlie 		/* Get BootAddress */
23183c6620bSDave Airlie 		ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
23283c6620bSDave Airlie 		data = ast_mindwm(ast, 0x1e6e0004);
23383c6620bSDave Airlie 		switch (data & 0x03) {
23483c6620bSDave Airlie 		case 0:
23583c6620bSDave Airlie 			boot_address = 0x44000000;
23683c6620bSDave Airlie 			break;
23783c6620bSDave Airlie 		default:
23883c6620bSDave Airlie 		case 1:
23983c6620bSDave Airlie 			boot_address = 0x48000000;
24083c6620bSDave Airlie 			break;
24183c6620bSDave Airlie 		case 2:
24283c6620bSDave Airlie 			boot_address = 0x50000000;
24383c6620bSDave Airlie 			break;
24483c6620bSDave Airlie 		case 3:
24583c6620bSDave Airlie 			boot_address = 0x60000000;
24683c6620bSDave Airlie 			break;
24783c6620bSDave Airlie 		}
24883c6620bSDave Airlie 		boot_address -= 0x200000; /* -2MB */
24983c6620bSDave Airlie 
25083c6620bSDave Airlie 		/* copy image to buffer */
25183c6620bSDave Airlie 		for (i = 0; i < len; i += 4) {
25283c6620bSDave Airlie 			data = *(u32 *)(fw_addr + i);
25383c6620bSDave Airlie 			ast_moutdwm(ast, boot_address + i, data);
25483c6620bSDave Airlie 		}
25583c6620bSDave Airlie 
25683c6620bSDave Airlie 		/* Init SCU */
25783c6620bSDave Airlie 		ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
25883c6620bSDave Airlie 
25983c6620bSDave Airlie 		/* Launch FW */
26083c6620bSDave Airlie 		ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address);
26183c6620bSDave Airlie 		ast_moutdwm(ast, 0x1e6e2100, 1);
26283c6620bSDave Airlie 
26383c6620bSDave Airlie 		/* Update Scratch */
26483c6620bSDave Airlie 		data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff;		/* D[11:9] = 100b: UEFI handling */
26583c6620bSDave Airlie 		data |= 0x800;
26683c6620bSDave Airlie 		ast_moutdwm(ast, 0x1e6e2040, data);
26783c6620bSDave Airlie 
26883c6620bSDave Airlie 		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
26983c6620bSDave Airlie 		jreg |= 0x02;
27083c6620bSDave Airlie 		ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg);
27183c6620bSDave Airlie 	}
27283c6620bSDave Airlie 	return true;
27383c6620bSDave Airlie }
27483c6620bSDave Airlie 
ast_dp501_is_connected(struct ast_device * ast)275*f81bb0acSJocelyn Falempe bool ast_dp501_is_connected(struct ast_device *ast)
27683c6620bSDave Airlie {
277*f81bb0acSJocelyn Falempe 	u32 boot_address, offset, data;
27883c6620bSDave Airlie 
279ba4e0339SKuoHsiang Chou 	if (ast->config_mode == ast_use_p2a) {
28083c6620bSDave Airlie 		boot_address = get_fw_base(ast);
28183c6620bSDave Airlie 
28283c6620bSDave Airlie 		/* validate FW version */
283ba4e0339SKuoHsiang Chou 		offset = AST_DP501_GBL_VERSION;
28483c6620bSDave Airlie 		data = ast_mindwm(ast, boot_address + offset);
285ba4e0339SKuoHsiang Chou 		if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1)
28683c6620bSDave Airlie 			return false;
28783c6620bSDave Airlie 
28883c6620bSDave Airlie 		/* validate PnP Monitor */
289ba4e0339SKuoHsiang Chou 		offset = AST_DP501_PNPMONITOR;
29083c6620bSDave Airlie 		data = ast_mindwm(ast, boot_address + offset);
291ba4e0339SKuoHsiang Chou 		if (!(data & AST_DP501_PNP_CONNECTED))
29283c6620bSDave Airlie 			return false;
293ba4e0339SKuoHsiang Chou 	} else {
294ba4e0339SKuoHsiang Chou 		if (!ast->dp501_fw_buf)
295ba4e0339SKuoHsiang Chou 			return false;
296ba4e0339SKuoHsiang Chou 
297ba4e0339SKuoHsiang Chou 		/* dummy read */
298ba4e0339SKuoHsiang Chou 		offset = 0x0000;
299ba4e0339SKuoHsiang Chou 		data = readl(ast->dp501_fw_buf + offset);
300ba4e0339SKuoHsiang Chou 
301ba4e0339SKuoHsiang Chou 		/* validate FW version */
302ba4e0339SKuoHsiang Chou 		offset = AST_DP501_GBL_VERSION;
303ba4e0339SKuoHsiang Chou 		data = readl(ast->dp501_fw_buf + offset);
304ba4e0339SKuoHsiang Chou 		if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1)
305ba4e0339SKuoHsiang Chou 			return false;
306ba4e0339SKuoHsiang Chou 
307ba4e0339SKuoHsiang Chou 		/* validate PnP Monitor */
308ba4e0339SKuoHsiang Chou 		offset = AST_DP501_PNPMONITOR;
309ba4e0339SKuoHsiang Chou 		data = readl(ast->dp501_fw_buf + offset);
310ba4e0339SKuoHsiang Chou 		if (!(data & AST_DP501_PNP_CONNECTED))
311ba4e0339SKuoHsiang Chou 			return false;
312*f81bb0acSJocelyn Falempe 	}
313*f81bb0acSJocelyn Falempe 	return true;
314*f81bb0acSJocelyn Falempe }
315ba4e0339SKuoHsiang Chou 
ast_dp501_read_edid(struct drm_device * dev,u8 * ediddata)316*f81bb0acSJocelyn Falempe bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
317*f81bb0acSJocelyn Falempe {
318*f81bb0acSJocelyn Falempe 	struct ast_device *ast = to_ast_device(dev);
319*f81bb0acSJocelyn Falempe 	u32 i, boot_address, offset, data;
320*f81bb0acSJocelyn Falempe 	u32 *pEDIDidx;
321*f81bb0acSJocelyn Falempe 
322*f81bb0acSJocelyn Falempe 	if (!ast_dp501_is_connected(ast))
323*f81bb0acSJocelyn Falempe 		return false;
324*f81bb0acSJocelyn Falempe 
325*f81bb0acSJocelyn Falempe 	if (ast->config_mode == ast_use_p2a) {
326*f81bb0acSJocelyn Falempe 		boot_address = get_fw_base(ast);
327*f81bb0acSJocelyn Falempe 
328*f81bb0acSJocelyn Falempe 		/* Read EDID */
329*f81bb0acSJocelyn Falempe 		offset = AST_DP501_EDID_DATA;
330*f81bb0acSJocelyn Falempe 		for (i = 0; i < 128; i += 4) {
331*f81bb0acSJocelyn Falempe 			data = ast_mindwm(ast, boot_address + offset + i);
332*f81bb0acSJocelyn Falempe 			pEDIDidx = (u32 *)(ediddata + i);
333*f81bb0acSJocelyn Falempe 			*pEDIDidx = data;
334*f81bb0acSJocelyn Falempe 		}
335*f81bb0acSJocelyn Falempe 	} else {
336ba4e0339SKuoHsiang Chou 		/* Read EDID */
337ba4e0339SKuoHsiang Chou 		offset = AST_DP501_EDID_DATA;
338ba4e0339SKuoHsiang Chou 		for (i = 0; i < 128; i += 4) {
339ba4e0339SKuoHsiang Chou 			data = readl(ast->dp501_fw_buf + offset + i);
340ba4e0339SKuoHsiang Chou 			pEDIDidx = (u32 *)(ediddata + i);
341ba4e0339SKuoHsiang Chou 			*pEDIDidx = data;
342ba4e0339SKuoHsiang Chou 		}
34383c6620bSDave Airlie 	}
34483c6620bSDave Airlie 
34583c6620bSDave Airlie 	return true;
34683c6620bSDave Airlie }
34783c6620bSDave Airlie 
ast_init_dvo(struct drm_device * dev)34883c6620bSDave Airlie static bool ast_init_dvo(struct drm_device *dev)
34983c6620bSDave Airlie {
3505abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
35183c6620bSDave Airlie 	u8 jreg;
35283c6620bSDave Airlie 	u32 data;
35383c6620bSDave Airlie 	ast_write32(ast, 0xf004, 0x1e6e0000);
35483c6620bSDave Airlie 	ast_write32(ast, 0xf000, 0x1);
35583c6620bSDave Airlie 	ast_write32(ast, 0x12000, 0x1688a8a8);
35683c6620bSDave Airlie 
35783c6620bSDave Airlie 	jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
35883c6620bSDave Airlie 	if (!(jreg & 0x80)) {
35983c6620bSDave Airlie 		/* Init SCU DVO Settings */
36083c6620bSDave Airlie 		data = ast_read32(ast, 0x12008);
36183c6620bSDave Airlie 		/* delay phase */
36283c6620bSDave Airlie 		data &= 0xfffff8ff;
36383c6620bSDave Airlie 		data |= 0x00000500;
36483c6620bSDave Airlie 		ast_write32(ast, 0x12008, data);
36583c6620bSDave Airlie 
366ecf64579SThomas Zimmermann 		if (IS_AST_GEN4(ast)) {
36783c6620bSDave Airlie 			data = ast_read32(ast, 0x12084);
36883c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
36983c6620bSDave Airlie 			data |= 0xfffe0000;
37083c6620bSDave Airlie 			ast_write32(ast, 0x12084, data);
37183c6620bSDave Airlie 
37283c6620bSDave Airlie 			data = ast_read32(ast, 0x12088);
37383c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
37483c6620bSDave Airlie 			data |= 0x000fffff;
37583c6620bSDave Airlie 			ast_write32(ast, 0x12088, data);
37683c6620bSDave Airlie 
37783c6620bSDave Airlie 			data = ast_read32(ast, 0x12090);
37883c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
37983c6620bSDave Airlie 			data &= 0xffffffcf;
38083c6620bSDave Airlie 			data |= 0x00000020;
38183c6620bSDave Airlie 			ast_write32(ast, 0x12090, data);
382ecf64579SThomas Zimmermann 		} else { /* AST GEN5+ */
38383c6620bSDave Airlie 			data = ast_read32(ast, 0x12088);
38483c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
38583c6620bSDave Airlie 			data |= 0x30000000;
38683c6620bSDave Airlie 			ast_write32(ast, 0x12088, data);
38783c6620bSDave Airlie 
38883c6620bSDave Airlie 			data = ast_read32(ast, 0x1208c);
38983c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
39083c6620bSDave Airlie 			data |= 0x000000cf;
39183c6620bSDave Airlie 			ast_write32(ast, 0x1208c, data);
39283c6620bSDave Airlie 
39383c6620bSDave Airlie 			data = ast_read32(ast, 0x120a4);
39483c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
39583c6620bSDave Airlie 			data |= 0xffff0000;
39683c6620bSDave Airlie 			ast_write32(ast, 0x120a4, data);
39783c6620bSDave Airlie 
39883c6620bSDave Airlie 			data = ast_read32(ast, 0x120a8);
39983c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
40083c6620bSDave Airlie 			data |= 0x0000000f;
40183c6620bSDave Airlie 			ast_write32(ast, 0x120a8, data);
40283c6620bSDave Airlie 
40383c6620bSDave Airlie 			data = ast_read32(ast, 0x12094);
40483c6620bSDave Airlie 			/* multi-pins for DVO single-edge */
40583c6620bSDave Airlie 			data |= 0x00000002;
40683c6620bSDave Airlie 			ast_write32(ast, 0x12094, data);
40783c6620bSDave Airlie 		}
40883c6620bSDave Airlie 	}
40983c6620bSDave Airlie 
41083c6620bSDave Airlie 	/* Force to DVO */
41183c6620bSDave Airlie 	data = ast_read32(ast, 0x1202c);
41283c6620bSDave Airlie 	data &= 0xfffbffff;
41383c6620bSDave Airlie 	ast_write32(ast, 0x1202c, data);
41483c6620bSDave Airlie 
41583c6620bSDave Airlie 	/* Init VGA DVO Settings */
41683c6620bSDave Airlie 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80);
41783c6620bSDave Airlie 	return true;
41883c6620bSDave Airlie }
41983c6620bSDave Airlie 
42037b9b81fSBenjamin Herrenschmidt 
ast_init_analog(struct drm_device * dev)42137b9b81fSBenjamin Herrenschmidt static void ast_init_analog(struct drm_device *dev)
42237b9b81fSBenjamin Herrenschmidt {
4235abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
42437b9b81fSBenjamin Herrenschmidt 	u32 data;
42537b9b81fSBenjamin Herrenschmidt 
42637b9b81fSBenjamin Herrenschmidt 	/*
42737b9b81fSBenjamin Herrenschmidt 	 * Set DAC source to VGA mode in SCU2C via the P2A
42837b9b81fSBenjamin Herrenschmidt 	 * bridge. First configure the P2U to target the SCU
42937b9b81fSBenjamin Herrenschmidt 	 * in case it isn't at this stage.
43037b9b81fSBenjamin Herrenschmidt 	 */
43137b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0xf004, 0x1e6e0000);
43237b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0xf000, 0x1);
43337b9b81fSBenjamin Herrenschmidt 
43437b9b81fSBenjamin Herrenschmidt 	/* Then unlock the SCU with the magic password */
43537b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0x12000, 0x1688a8a8);
43637b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0x12000, 0x1688a8a8);
43737b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0x12000, 0x1688a8a8);
43837b9b81fSBenjamin Herrenschmidt 
43937b9b81fSBenjamin Herrenschmidt 	/* Finally, clear bits [17:16] of SCU2c */
44037b9b81fSBenjamin Herrenschmidt 	data = ast_read32(ast, 0x1202c);
44137b9b81fSBenjamin Herrenschmidt 	data &= 0xfffcffff;
44237b9b81fSBenjamin Herrenschmidt 	ast_write32(ast, 0, data);
44337b9b81fSBenjamin Herrenschmidt 
44437b9b81fSBenjamin Herrenschmidt 	/* Disable DVO */
44537b9b81fSBenjamin Herrenschmidt 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00);
44637b9b81fSBenjamin Herrenschmidt }
44737b9b81fSBenjamin Herrenschmidt 
ast_init_3rdtx(struct drm_device * dev)44883c6620bSDave Airlie void ast_init_3rdtx(struct drm_device *dev)
44983c6620bSDave Airlie {
4505abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
45183c6620bSDave Airlie 	u8 jreg;
45237b9b81fSBenjamin Herrenschmidt 
453ecf64579SThomas Zimmermann 	if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) {
45483c6620bSDave Airlie 		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
45583c6620bSDave Airlie 		switch (jreg & 0x0e) {
45683c6620bSDave Airlie 		case 0x04:
45783c6620bSDave Airlie 			ast_init_dvo(dev);
45883c6620bSDave Airlie 			break;
45983c6620bSDave Airlie 		case 0x08:
46083c6620bSDave Airlie 			ast_launch_m68k(dev);
46183c6620bSDave Airlie 			break;
46283c6620bSDave Airlie 		case 0x0c:
46383c6620bSDave Airlie 			ast_init_dvo(dev);
46483c6620bSDave Airlie 			break;
46583c6620bSDave Airlie 		default:
4667f35680aSThomas Zimmermann 			if (ast->tx_chip_types & BIT(AST_TX_SIL164))
46783c6620bSDave Airlie 				ast_init_dvo(dev);
46837b9b81fSBenjamin Herrenschmidt 			else
46937b9b81fSBenjamin Herrenschmidt 				ast_init_analog(dev);
47083c6620bSDave Airlie 		}
47183c6620bSDave Airlie 	}
47283c6620bSDave Airlie }
473