Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d40d9187 authored by Daniel Vetter's avatar Daniel Vetter
Browse files

Merge branch 'topic/drm-vblank-rework' into drm-intel-next-queued



Pull in the drm vblank rework from Ville and me. drm core parts acked
by Dave Airlie

Conflicts:
	drivers/gpu/drm/i915/intel_display.c

Just a bit of fun around the placement of drm_vblank_on. This merge
resolution has been tested in drm-intel-nightly for a while already.

Acked-by: default avatarDave Airlie <airlied@gmail.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parents 4fa62c89 c5ab3bc0
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -1895,7 +1895,7 @@ void intel_crt_init(struct drm_device *dev)
          <para>
            The function filters out modes larger than
            <parameter>max_width</parameter> and <parameter>max_height</parameter>
            if specified. It then calls the connector
            if specified. It then calls the optional connector
            <methodname>mode_valid</methodname> helper operation for each mode in
            the probed list to check whether the mode is valid for the connector.
          </para>
@@ -2257,7 +2257,7 @@ void intel_crt_init(struct drm_device *dev)
          <para>
            Verify whether a mode is valid for the connector. Return MODE_OK for
            supported modes and one of the enum drm_mode_status values (MODE_*)
            for unsupported modes. This operation is mandatory.
            for unsupported modes. This operation is optional.
          </para>
          <para>
            As the mode rejection reason is currently not used beside for
@@ -2519,6 +2519,10 @@ void (*disable_vblank) (struct drm_device *dev, int crtc);</synopsis>
      with a call to <function>drm_vblank_cleanup</function> in the driver
      <methodname>unload</methodname> operation handler.
    </para>
    <sect2>
      <title>Vertical Blanking and Interrupt Handling Functions Reference</title>
!Edrivers/gpu/drm/drm_irq.c
    </sect2>
  </sect1>

  <!-- Internals: open/close, file operations and ioctls -->
@@ -2861,17 +2865,16 @@ int num_ioctls;</synopsis>
            <term>DRM_IOCTL_MODESET_CTL</term>
            <listitem>
              <para>
                This should be called by application level drivers before and
                after mode setting, since on many devices the vertical blank
                counter is reset at that time.  Internally, the DRM snapshots
                the last vblank count when the ioctl is called with the
                _DRM_PRE_MODESET command, so that the counter won't go backwards
                (which is dealt with when _DRM_POST_MODESET is used).
		This was only used for user-mode-settind drivers around
		modesetting changes to allow the kernel to update the vblank
		interrupt after mode setting, since on many devices the vertical
		blank counter is reset to 0 at some point during modeset. Modern
		drivers should not call this any more since with kernel mode
		setting it is a no-op.
              </para>
            </listitem>
          </varlistentry>
        </variablelist>
<!--!Edrivers/char/drm/drm_irq.c-->
      </para>
    </sect1>

+2 −2
Original line number Diff line number Diff line
@@ -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
+410 −0
Original line number Diff line number Diff line

#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);
			}
		}
	}
}
+24 −0
Original line number Diff line number Diff line
@@ -61,9 +61,17 @@ enum ast_chip {
	AST2200,
	AST2150,
	AST2300,
	AST2400,
	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
@@ -102,6 +110,12 @@ struct ast_private {
	 * we have. */
	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);
@@ -368,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
+82 −8
Original line number Diff line number Diff line
@@ -66,12 +66,16 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast,
static int ast_detect_chip(struct drm_device *dev)
{
	struct ast_private *ast = dev->dev_private;
	uint32_t data, jreg;

	if (dev->pdev->device == PCI_CHIP_AST1180) {
		ast->chip = AST1100;
		DRM_INFO("AST 1180 detected\n");
	} else {
		if (dev->pdev->revision >= 0x20) {
		if (dev->pdev->revision >= 0x30) {
			ast->chip = AST2400;
			DRM_INFO("AST 2400 detected\n");
		} else if (dev->pdev->revision >= 0x20) {
			ast->chip = AST2300;
			DRM_INFO("AST 2300 detected\n");
		} else if (dev->pdev->revision >= 0x10) {
@@ -104,6 +108,59 @@ static int ast_detect_chip(struct drm_device *dev)
			DRM_INFO("AST 2000 detected\n");
		}
	}

	switch (ast->chip) {
	case AST1180:
		ast->support_wide_screen = true;
		break;
	case AST2000:
		ast->support_wide_screen = false;
		break;
	default:
		jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
		if (!(jreg & 0x80))
			ast->support_wide_screen = true;
		else if (jreg & 0x01)
			ast->support_wide_screen = true;
		else {
			ast->support_wide_screen = false;
			ast_write32(ast, 0xf004, 0x1e6e0000);
			ast_write32(ast, 0xf000, 0x1);
			data = ast_read32(ast, 0x1207c);
			data &= 0x300;
			if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
				ast->support_wide_screen = true;
			if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
				ast->support_wide_screen = true;
		}
		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;
}

@@ -129,7 +186,7 @@ static int ast_get_dram_info(struct drm_device *dev)
	else
		ast->dram_bus_width = 32;

	if (ast->chip == AST2300) {
	if (ast->chip == AST2300 || ast->chip == AST2400) {
		switch (data & 0x03) {
		case 0:
			ast->dram_type = AST_DRAM_512Mx16;
@@ -257,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)
@@ -316,6 +388,7 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
	if (ast->chip == AST2100 ||
	    ast->chip == AST2200 ||
	    ast->chip == AST2300 ||
	    ast->chip == AST2400 ||
	    ast->chip == AST1180) {
		dev->mode_config.max_width = 1920;
		dev->mode_config.max_height = 2048;
@@ -343,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);
Loading