diff options
| author | Dave Airlie <airlied@redhat.com> | 2014-03-28 11:05:12 +1000 | 
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2014-05-19 11:13:57 +1000 | 
| commit | 83c6620bae3f14adb2430fdcc367980fe3b7bee2 (patch) | |
| tree | b7d9d9d1d70c2ae5fb5479bdd16b7f04d9282892 /drivers | |
| parent | 0e5ce92438146655d60447802d2c11bdbc089329 (diff) | |
drm/ast: initial DP501 support (v0.2)
This is the initial attempt at porting the DP501 code from the userspace
driver,
the firmware file is in
http://people.freedesktop.org/~airlied/ast_dp501_fw.bin
this should really be exposed as another encoder/connector that is cloneable
v0.2:
init 3rd tx properly,
add scratch reduction of VRAM size
backup firmware properly.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gpu/drm/ast/Makefile | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/ast/ast_dp501.c | 410 | ||||
| -rw-r--r-- | drivers/gpu/drm/ast/ast_drv.h | 22 | ||||
| -rw-r--r-- | drivers/gpu/drm/ast/ast_main.c | 53 | ||||
| -rw-r--r-- | drivers/gpu/drm/ast/ast_mode.c | 22 | ||||
| -rw-r--r-- | drivers/gpu/drm/ast/ast_post.c | 6 | 
6 files changed, 505 insertions, 12 deletions
| diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 8df4f284ee24..171aa0622b66 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -4,6 +4,6 @@  ccflags-y := -Iinclude/drm -ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o +ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o ast_dp501.o -obj-$(CONFIG_DRM_AST) := ast.o
\ No newline at end of file +obj-$(CONFIG_DRM_AST) := ast.o diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c new file mode 100644 index 000000000000..5da4b62285fa --- /dev/null +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -0,0 +1,410 @@ + +#include <linux/firmware.h> +#include <drm/drmP.h> +#include "ast_drv.h" +MODULE_FIRMWARE("ast_dp501_fw.bin"); + +int ast_load_dp501_microcode(struct drm_device *dev) +{ +	struct ast_private *ast = dev->dev_private; +	static char *fw_name = "ast_dp501_fw.bin"; +	int err; +	err = request_firmware(&ast->dp501_fw, fw_name, dev->dev); +	if (err) +		return err; + +	return 0; +} + +static void send_ack(struct ast_private *ast) +{ +	u8 sendack; +	sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); +	sendack |= 0x80; +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); +} + +static void send_nack(struct ast_private *ast) +{ +	u8 sendack; +	sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); +	sendack &= ~0x80; +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); +} + +static bool wait_ack(struct ast_private *ast) +{ +	u8 waitack; +	u32 retry = 0; +	do { +		waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); +		waitack &= 0x80; +		udelay(100); +	} while ((!waitack) && (retry++ < 1000)); + +	if (retry < 1000) +		return true; +	else +		return false; +} + +static bool wait_nack(struct ast_private *ast) +{ +	u8 waitack; +	u32 retry = 0; +	do { +		waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); +		waitack &= 0x80; +		udelay(100); +	} while ((waitack) && (retry++ < 1000)); + +	if (retry < 1000) +		return true; +	else +		return false; +} + +static void set_cmd_trigger(struct ast_private *ast) +{ +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40); +} + +static void clear_cmd_trigger(struct ast_private *ast) +{ +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00); +} + +#if 0 +static bool wait_fw_ready(struct ast_private *ast) +{ +	u8 waitready; +	u32 retry = 0; +	do { +		waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); +		waitready &= 0x40; +		udelay(100); +	} while ((!waitready) && (retry++ < 1000)); + +	if (retry < 1000) +		return true; +	else +		return false; +} +#endif + +static bool ast_write_cmd(struct drm_device *dev, u8 data) +{ +	struct ast_private *ast = dev->dev_private; +	int retry = 0; +	if (wait_nack(ast)) { +		send_nack(ast); +		ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data); +		send_ack(ast); +		set_cmd_trigger(ast); +		do { +			if (wait_ack(ast)) { +				clear_cmd_trigger(ast); +				send_nack(ast); +				return true; +			} +		} while (retry++ < 100); +	} +	clear_cmd_trigger(ast); +	send_nack(ast); +	return false; +} + +static bool ast_write_data(struct drm_device *dev, u8 data) +{ +	struct ast_private *ast = dev->dev_private; + +	if (wait_nack(ast)) { +		send_nack(ast); +		ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data); +		send_ack(ast); +		if (wait_ack(ast)) { +			send_nack(ast); +			return true; +		} +	} +	send_nack(ast); +	return false; +} + +#if 0 +static bool ast_read_data(struct drm_device *dev, u8 *data) +{ +	struct ast_private *ast = dev->dev_private; +	u8 tmp; + +	*data = 0; + +	if (wait_ack(ast) == false) +		return false; +	tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff); +	*data = tmp; +	if (wait_nack(ast) == false) { +		send_nack(ast); +		return false; +	} +	send_nack(ast); +	return true; +} + +static void clear_cmd(struct ast_private *ast) +{ +	send_nack(ast); +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00); +} +#endif + +void ast_set_dp501_video_output(struct drm_device *dev, u8 mode) +{ +	ast_write_cmd(dev, 0x40); +	ast_write_data(dev, mode); + +	msleep(10); +} + +static u32 get_fw_base(struct ast_private *ast) +{ +	return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff; +} + +bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) +{ +	struct ast_private *ast = dev->dev_private; +	u32 i, data; +	u32 boot_address; + +	data = ast_mindwm(ast, 0x1e6e2100) & 0x01; +	if (data) { +		boot_address = get_fw_base(ast); +		for (i = 0; i < size; i += 4) +			*(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i); +		return true; +	} +	return false; +} + +bool ast_launch_m68k(struct drm_device *dev) +{ +	struct ast_private *ast = dev->dev_private; +	u32 i, data, len = 0; +	u32 boot_address; +	u8 *fw_addr = NULL; +	u8 jreg; + +	data = ast_mindwm(ast, 0x1e6e2100) & 0x01; +	if (!data) { + +		if (ast->dp501_fw_addr) { +			fw_addr = ast->dp501_fw_addr; +			len = 32*1024; +		} else if (ast->dp501_fw) { +			fw_addr = (u8 *)ast->dp501_fw->data; +			len = ast->dp501_fw->size; +		} +		/* Get BootAddress */ +		ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8); +		data = ast_mindwm(ast, 0x1e6e0004); +		switch (data & 0x03) { +		case 0: +			boot_address = 0x44000000; +			break; +		default: +		case 1: +			boot_address = 0x48000000; +			break; +		case 2: +			boot_address = 0x50000000; +			break; +		case 3: +			boot_address = 0x60000000; +			break; +		} +		boot_address -= 0x200000; /* -2MB */ + +		/* copy image to buffer */ +		for (i = 0; i < len; i += 4) { +			data = *(u32 *)(fw_addr + i); +			ast_moutdwm(ast, boot_address + i, data); +		} + +		/* Init SCU */ +		ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8); + +		/* Launch FW */ +		ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address); +		ast_moutdwm(ast, 0x1e6e2100, 1); + +		/* Update Scratch */ +		data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff;		/* D[11:9] = 100b: UEFI handling */ +		data |= 0x800; +		ast_moutdwm(ast, 0x1e6e2040, data); + +		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */ +		jreg |= 0x02; +		ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg); +	} +	return true; +} + +u8 ast_get_dp501_max_clk(struct drm_device *dev) +{ +	struct ast_private *ast = dev->dev_private; +	u32 boot_address, offset, data; +	u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; + +	boot_address = get_fw_base(ast); + +	/* validate FW version */ +	offset = 0xf000; +	data = ast_mindwm(ast, boot_address + offset); +	if ((data & 0xf0) != 0x10) /* version: 1x */ +		return maxclk; + +	/* Read Link Capability */ +	offset  = 0xf014; +	*(u32 *)linkcap = ast_mindwm(ast, boot_address + offset); +	if (linkcap[2] == 0) { +		linkrate = linkcap[0]; +		linklanes = linkcap[1]; +		data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); +		if (data > 0xff) +			data = 0xff; +		maxclk = (u8)data; +	} +	return maxclk; +} + +bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) +{ +	struct ast_private *ast = dev->dev_private; +	u32 i, boot_address, offset, data; + +	boot_address = get_fw_base(ast); + +	/* validate FW version */ +	offset = 0xf000; +	data = ast_mindwm(ast, boot_address + offset); +	if ((data & 0xf0) != 0x10) +		return false; + +	/* validate PnP Monitor */ +	offset = 0xf010; +	data = ast_mindwm(ast, boot_address + offset); +	if (!(data & 0x01)) +		return false; + +	/* Read EDID */ +	offset = 0xf020; +	for (i = 0; i < 128; i += 4) { +		data = ast_mindwm(ast, boot_address + offset + i); +		*(u32 *)(ediddata + i) = data; +	} + +	return true; +} + +static bool ast_init_dvo(struct drm_device *dev) +{ +	struct ast_private *ast = dev->dev_private; +	u8 jreg; +	u32 data; +	ast_write32(ast, 0xf004, 0x1e6e0000); +	ast_write32(ast, 0xf000, 0x1); +	ast_write32(ast, 0x12000, 0x1688a8a8); + +	jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); +	if (!(jreg & 0x80)) { +		/* Init SCU DVO Settings */ +		data = ast_read32(ast, 0x12008); +		/* delay phase */ +		data &= 0xfffff8ff; +		data |= 0x00000500; +		ast_write32(ast, 0x12008, data); + +		if (ast->chip == AST2300) { +			data = ast_read32(ast, 0x12084); +			/* multi-pins for DVO single-edge */ +			data |= 0xfffe0000; +			ast_write32(ast, 0x12084, data); + +			data = ast_read32(ast, 0x12088); +			/* multi-pins for DVO single-edge */ +			data |= 0x000fffff; +			ast_write32(ast, 0x12088, data); + +			data = ast_read32(ast, 0x12090); +			/* multi-pins for DVO single-edge */ +			data &= 0xffffffcf; +			data |= 0x00000020; +			ast_write32(ast, 0x12090, data); +		} else { /* AST2400 */ +			data = ast_read32(ast, 0x12088); +			/* multi-pins for DVO single-edge */ +			data |= 0x30000000; +			ast_write32(ast, 0x12088, data); + +			data = ast_read32(ast, 0x1208c); +			/* multi-pins for DVO single-edge */ +			data |= 0x000000cf; +			ast_write32(ast, 0x1208c, data); + +			data = ast_read32(ast, 0x120a4); +			/* multi-pins for DVO single-edge */ +			data |= 0xffff0000; +			ast_write32(ast, 0x120a4, data); + +			data = ast_read32(ast, 0x120a8); +			/* multi-pins for DVO single-edge */ +			data |= 0x0000000f; +			ast_write32(ast, 0x120a8, data); + +			data = ast_read32(ast, 0x12094); +			/* multi-pins for DVO single-edge */ +			data |= 0x00000002; +			ast_write32(ast, 0x12094, data); +		} +	} + +	/* Force to DVO */ +	data = ast_read32(ast, 0x1202c); +	data &= 0xfffbffff; +	ast_write32(ast, 0x1202c, data); + +	/* Init VGA DVO Settings */ +	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); +	return true; +} + +void ast_init_3rdtx(struct drm_device *dev) +{ +	struct ast_private *ast = dev->dev_private; +	u8 jreg; +	u32 data; +	if (ast->chip == AST2300 || ast->chip == AST2400) { +		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); +		switch (jreg & 0x0e) { +		case 0x04: +			ast_init_dvo(dev); +			break; +		case 0x08: +			ast_launch_m68k(dev); +			break; +		case 0x0c: +			ast_init_dvo(dev); +			break; +		default: +			if (ast->tx_chip_type == AST_TX_SIL164) +				ast_init_dvo(dev); +			else { +				ast_write32(ast, 0x12000, 0x1688a8a8); +				data = ast_read32(ast, 0x1202c); +				data &= 0xfffcffff; +				ast_write32(ast, 0, data); +			} +		} +	} +} diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 561742793947..5d6a87573c33 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -65,6 +65,13 @@ enum ast_chip {  	AST1180,  }; +enum ast_tx_chip { +	AST_TX_NONE, +	AST_TX_SIL164, +	AST_TX_ITE66121, +	AST_TX_DP501, +}; +  #define AST_DRAM_512Mx16 0  #define AST_DRAM_1Gx16   1  #define AST_DRAM_512Mx32 2 @@ -104,6 +111,11 @@ struct ast_private {  	struct ttm_bo_kmap_obj cache_kmap;  	int next_cursor;  	bool support_wide_screen; + +	enum ast_tx_chip tx_chip_type; +	u8 dp501_maxclk; +	u8 *dp501_fw_addr; +	const struct firmware *dp501_fw;	/* dp501 fw */  };  int ast_driver_load(struct drm_device *dev, unsigned long flags); @@ -370,4 +382,14 @@ int ast_mmap(struct file *filp, struct vm_area_struct *vma);  /* ast post */  void ast_post_gpu(struct drm_device *dev); +u32 ast_mindwm(struct ast_private *ast, u32 r); +void ast_moutdwm(struct ast_private *ast, u32 r, u32 v); +/* ast dp501 */ +int ast_load_dp501_microcode(struct drm_device *dev); +void ast_set_dp501_video_output(struct drm_device *dev, u8 mode); +bool ast_launch_m68k(struct drm_device *dev); +bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size); +bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata); +u8 ast_get_dp501_max_clk(struct drm_device *dev); +void ast_init_3rdtx(struct drm_device *dev);  #endif diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 01ea4b6d4bf3..1124fb40758e 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -136,6 +136,31 @@ static int ast_detect_chip(struct drm_device *dev)  		break;  	} +	ast->tx_chip_type = AST_TX_NONE; +	jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); +	if (jreg & 0x80) +		ast->tx_chip_type = AST_TX_SIL164; +	if ((ast->chip == AST2300) || (ast->chip == AST2400)) { +		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); +		switch (jreg) { +		case 0x04: +			ast->tx_chip_type = AST_TX_SIL164; +			break; +		case 0x08: +			ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL); +			if (ast->dp501_fw_addr) { +				/* backup firmware */ +				if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) { +					kfree(ast->dp501_fw_addr); +					ast->dp501_fw_addr = NULL; +				} +			} +			/* fallthrough */ +		case 0x0c: +			ast->tx_chip_type = AST_TX_DP501; +		} +	} +  	return 0;  } @@ -289,17 +314,32 @@ static u32 ast_get_vram_info(struct drm_device *dev)  {  	struct ast_private *ast = dev->dev_private;  	u8 jreg; - +	u32 vram_size;  	ast_open_key(ast); +	vram_size = AST_VIDMEM_DEFAULT_SIZE;  	jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);  	switch (jreg & 3) { -	case 0: return AST_VIDMEM_SIZE_8M; -	case 1: return AST_VIDMEM_SIZE_16M; -	case 2: return AST_VIDMEM_SIZE_32M; -	case 3: return AST_VIDMEM_SIZE_64M; +	case 0: vram_size = AST_VIDMEM_SIZE_8M; break; +	case 1: vram_size = AST_VIDMEM_SIZE_16M; break; +	case 2: vram_size = AST_VIDMEM_SIZE_32M; break; +	case 3: vram_size = AST_VIDMEM_SIZE_64M; break;  	} -	return AST_VIDMEM_DEFAULT_SIZE; + +	jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); +	switch (jreg & 0x03) { +	case 1: +		vram_size -= 0x100000; +		break; +	case 2: +		vram_size -= 0x200000; +		break; +	case 3: +		vram_size -= 0x400000; +		break; +	} + +	return vram_size;  }  int ast_driver_load(struct drm_device *dev, unsigned long flags) @@ -376,6 +416,7 @@ int ast_driver_unload(struct drm_device *dev)  {  	struct ast_private *ast = dev->dev_private; +	kfree(ast->dp501_fw_addr);  	ast_mode_fini(dev);  	ast_fbdev_fini(dev);  	drm_mode_config_cleanup(dev); diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index e9a14a14a029..208dc45d0513 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -460,9 +460,13 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)  	case DRM_MODE_DPMS_STANDBY:  	case DRM_MODE_DPMS_SUSPEND:  		ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0); +		if (ast->tx_chip_type == AST_TX_DP501) +			ast_set_dp501_video_output(crtc->dev, 1);  		ast_crtc_load_lut(crtc);  		break;  	case DRM_MODE_DPMS_OFF: +		if (ast->tx_chip_type == AST_TX_DP501) +			ast_set_dp501_video_output(crtc->dev, 0);  		ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20);  		break;  	} @@ -738,10 +742,24 @@ static int ast_encoder_init(struct drm_device *dev)  static int ast_get_modes(struct drm_connector *connector)  {  	struct ast_connector *ast_connector = to_ast_connector(connector); +	struct ast_private *ast = connector->dev->dev_private;  	struct edid *edid;  	int ret; - -	edid = drm_get_edid(connector, &ast_connector->i2c->adapter); +	bool flags = false; +	if (ast->tx_chip_type == AST_TX_DP501) { +		ast->dp501_maxclk = 0xff; +		edid = kmalloc(128, GFP_KERNEL); +		if (!edid) +			return -ENOMEM; + +		flags = ast_dp501_read_edid(connector->dev, (u8 *)edid); +		if (flags) +			ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev); +		else +			kfree(edid); +	} +	if (!flags) +		edid = drm_get_edid(connector, &ast_connector->i2c->adapter);  	if (edid) {  		drm_mode_connector_update_edid_property(&ast_connector->base, edid);  		ret = drm_add_edid_modes(connector, edid); diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index e8a64383256e..116c8301dfd4 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -107,7 +107,7 @@ ast_set_def_ext_reg(struct drm_device *dev)  	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg);  } -static u32 ast_mindwm(struct ast_private *ast, u32 r) +u32 ast_mindwm(struct ast_private *ast, u32 r)  {  	uint32_t data; @@ -120,7 +120,7 @@ static u32 ast_mindwm(struct ast_private *ast, u32 r)  	return ast_read32(ast, 0x10000 + (r & 0x0000ffff));  } -static void ast_moutdwm(struct ast_private *ast, u32 r, u32 v) +void ast_moutdwm(struct ast_private *ast, u32 r, u32 v)  {  	uint32_t data;  	ast_write32(ast, 0xf004, r & 0xffff0000); @@ -378,6 +378,8 @@ void ast_post_gpu(struct drm_device *dev)  		ast_init_dram_2300(dev);  	else  		ast_init_dram_reg(dev); + +	ast_init_3rdtx(dev);  }  /* AST 2300 DRAM settings */ | 
