diff options
41 files changed, 1513 insertions, 873 deletions
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 1c51d43f254..62573c17091 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1895,8 +1895,8 @@ 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 - <methodname>mode_valid</methodname> helper operation for each mode in + 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> </listitem> @@ -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> diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 8df4f284ee2..171aa0622b6 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 00000000000..5da4b62285f --- /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 9833a1b1acc..5d6a87573c3 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -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 diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 01bf9e730ac..a2cc6be9798 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -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); diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index a4afdc8bb57..114aee941d4 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -115,11 +115,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo else vbios_mode->enh_table = &res_1280x1024[refresh_rate_index]; break; + case 1360: + vbios_mode->enh_table = &res_1360x768[refresh_rate_index]; + break; case 1440: vbios_mode->enh_table = &res_1440x900[refresh_rate_index]; break; case 1600: - vbios_mode->enh_table = &res_1600x1200[refresh_rate_index]; + if (crtc->mode.crtc_vdisplay == 900) + vbios_mode->enh_table = &res_1600x900[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1600x1200[refresh_rate_index]; break; case 1680: vbios_mode->enh_table = &res_1680x1050[refresh_rate_index]; @@ -175,14 +181,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00); + if (vbios_mode->enh_table->flags & NewModeInfo) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8); + } } return true; @@ -389,7 +398,7 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8); /* Set Threshold */ - if (ast->chip == AST2300) { + if (ast->chip == AST2300 || ast->chip == AST2400) { ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78); ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60); } else if (ast->chip == AST2100 || @@ -451,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; } @@ -729,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); @@ -746,7 +773,56 @@ static int ast_get_modes(struct drm_connector *connector) static int ast_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - return MODE_OK; + struct ast_private *ast = connector->dev->dev_private; + int flags = MODE_NOMODE; + uint32_t jtemp; + + if (ast->support_wide_screen) { + if ((mode->hdisplay == 1680) && (mode->vdisplay == 1050)) + return MODE_OK; + if ((mode->hdisplay == 1280) && (mode->vdisplay == 800)) + return MODE_OK; + if ((mode->hdisplay == 1440) && (mode->vdisplay == 900)) + return MODE_OK; + if ((mode->hdisplay == 1360) && (mode->vdisplay == 768)) + return MODE_OK; + if ((mode->hdisplay == 1600) && (mode->vdisplay == 900)) + return MODE_OK; + + if ((ast->chip == AST2100) || (ast->chip == AST2200) || (ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST1180)) { + if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080)) + return MODE_OK; + + if ((mode->hdisplay == 1920) && (mode->vdisplay == 1200)) { + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (jtemp & 0x01) + return MODE_NOMODE; + else + return MODE_OK; + } + } + } + switch (mode->hdisplay) { + case 640: + if (mode->vdisplay == 480) flags = MODE_OK; + break; + case 800: + if (mode->vdisplay == 600) flags = MODE_OK; + break; + case 1024: + if (mode->vdisplay == 768) flags = MODE_OK; + break; + case 1280: + if (mode->vdisplay == 1024) flags = MODE_OK; + break; + case 1600: + if (mode->vdisplay == 1200) flags = MODE_OK; + break; + default: + return flags; + } + + return flags; } static void ast_connector_destroy(struct drm_connector *connector) diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 635f6ffc27c..4e5ea3898e7 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -78,7 +78,7 @@ ast_set_def_ext_reg(struct drm_device *dev) for (i = 0x81; i <= 0x8f; i++) ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00); - if (ast->chip == AST2300) { + if (ast->chip == AST2300 || ast->chip == AST2400) { if (dev->pdev->revision >= 0x20) ext_reg_info = extreginfo_ast2300; else @@ -102,23 +102,32 @@ ast_set_def_ext_reg(struct drm_device *dev) /* Enable RAMDAC for A1 */ reg = 0x04; - if (ast->chip == AST2300) + if (ast->chip == AST2300 || ast->chip == AST2400) reg |= 0x20; ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg); } -static inline u32 mindwm(struct ast_private *ast, u32 r) +u32 ast_mindwm(struct ast_private *ast, u32 r) { + uint32_t data; + ast_write32(ast, 0xf004, r & 0xffff0000); ast_write32(ast, 0xf000, 0x1); + do { + data = ast_read32(ast, 0xf004) & 0xffff0000; + } while (data != (r & 0xffff0000)); return ast_read32(ast, 0x10000 + (r & 0x0000ffff)); } -static inline void 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); ast_write32(ast, 0xf000, 0x1); + do { + data = ast_read32(ast, 0xf004) & 0xffff0000; + } while (data != (r & 0xffff0000)); ast_write32(ast, 0x10000 + (r & 0x0000ffff), v); } @@ -154,28 +163,28 @@ static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x40; + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; if (++timeout > TIMEOUT_AST2150) { - moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return 0xffffffff; } } while (!data); - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x40; + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; if (++timeout > TIMEOUT_AST2150) { - moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return 0xffffffff; } } while (!data); - data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7; - moutdwm(ast, 0x1e6e0070, 0x00000000); + data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return data; } @@ -184,18 +193,18 @@ static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x40; + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; if (++timeout > TIMEOUT_AST2150) { - moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return 0xffffffff; } } while (!data); - data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7; - moutdwm(ast, 0x1e6e0070, 0x00000000); + data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return data; } #endif @@ -215,7 +224,7 @@ static int cbrscan_ast2150(struct ast_private *ast, int busw) u32 patcnt, loop; for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) { - moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]); + ast_moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]); for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) { if (cbrtest_ast2150(ast)) break; @@ -237,7 +246,7 @@ cbr_start: passcnt = 0; for (dlli = 0; dlli < 100; dlli++) { - moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); data = cbrscan_ast2150(ast, busw); if (data != 0) { if (data & 0x1) { @@ -254,7 +263,7 @@ cbr_start: goto cbr_start; dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4); - moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); } @@ -365,10 +374,12 @@ void ast_post_gpu(struct drm_device *dev) ast_open_key(ast); ast_set_def_ext_reg(dev); - if (ast->chip == AST2300) + if (ast->chip == AST2300 || ast->chip == AST2400) ast_init_dram_2300(dev); else ast_init_dram_reg(dev); + + ast_init_3rdtx(dev); } /* AST 2300 DRAM settings */ @@ -403,6 +414,7 @@ struct ast2300_dram_param { /* * DQSI DLL CBR Setting */ +#define CBR_SIZE0 ((1 << 10) - 1) #define CBR_SIZE1 ((4 << 10) - 1) #define CBR_SIZE2 ((64 << 10) - 1) #define CBR_PASSNUM 5 @@ -423,88 +435,84 @@ static const u32 pattern[8] = { 0x7C61D253 }; -#if 0 /* unused in DDX, included for completeness */ static int mmc_test_burst(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x3000; + data = ast_mindwm(ast, 0x1e6e0070) & 0x3000; if (data & 0x2000) { return 0; } if (++timeout > TIMEOUT) { - moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return 0; } } while (!data); - moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); return 1; } -#endif static int mmc_test_burst2(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x1000; + data = ast_mindwm(ast, 0x1e6e0070) & 0x1000; if (++timeout > TIMEOUT) { - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return -1; } } while (!data); - data = mindwm(ast, 0x1e6e0078); + data = ast_mindwm(ast, 0x1e6e0078); data = (data | (data >> 16)) & 0xffff; - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return data; } -#if 0 /* Unused in DDX here for completeness */ static int mmc_test_single(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x3000; + data = ast_mindwm(ast, 0x1e6e0070) & 0x3000; if (data & 0x2000) return 0; if (++timeout > TIMEOUT) { - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return 0; } } while (!data); - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return 1; } -#endif static int mmc_test_single2(struct ast_private *ast, u32 datagen) { u32 data, timeout; - moutdwm(ast, 0x1e6e0070, 0x00000000); - moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); timeout = 0; do { - data = mindwm(ast, 0x1e6e0070) & 0x1000; + data = ast_mindwm(ast, 0x1e6e0070) & 0x1000; if (++timeout > TIMEOUT) { - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return -1; } } while (!data); - data = mindwm(ast, 0x1e6e0078); + data = ast_mindwm(ast, 0x1e6e0078); data = (data | (data >> 16)) & 0xffff; - moutdwm(ast, 0x1e6e0070, 0x0); + ast_moutdwm(ast, 0x1e6e0070, 0x0); return data; } @@ -533,7 +541,7 @@ static int cbr_scan(struct ast_private *ast) data2 = 3; for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { - moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); for (loop = 0; loop < CBR_PASSNUM2; loop++) { if ((data = cbr_test(ast)) != 0) { data2 &= data; @@ -568,7 +576,7 @@ static u32 cbr_scan2(struct ast_private *ast) data2 = 0xffff; for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { - moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); for (loop = 0; loop < CBR_PASSNUM2; loop++) { if ((data = cbr_test2(ast)) != 0) { data2 &= data; @@ -583,106 +591,35 @@ static u32 cbr_scan2(struct ast_private *ast) return data2; } -#if 0 /* unused in DDX - added for completeness */ -static void finetuneDQI(struct ast_private *ast, struct ast2300_dram_param *param) +static u32 cbr_test3(struct ast_private *ast) { - u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt; - - gold_sadj[0] = (mindwm(ast, 0x1E6E0024) >> 16) & 0xffff; - gold_sadj[1] = gold_sadj[0] >> 8; - gold_sadj[0] = gold_sadj[0] & 0xff; - gold_sadj[0] = (gold_sadj[0] + gold_sadj[1]) >> 1; - gold_sadj[1] = gold_sadj[0]; - - for (cnt = 0; cnt < 16; cnt++) { - dllmin[cnt] = 0xff; - dllmax[cnt] = 0x0; - } - passcnt = 0; - for (dlli = 0; dlli < 76; dlli++) { - moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); - /* Wait DQSI latch phase calibration */ - moutdwm(ast, 0x1E6E0074, 0x00000010); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); + if (!mmc_test_burst(ast, 0)) + return 0; + if (!mmc_test_single(ast, 0)) + return 0; + return 1; +} - moutdwm(ast, 0x1E6E0074, CBR_SIZE1); - data = cbr_scan2(ast); - if (data != 0) { - mask = 0x00010001; - for (cnt = 0; cnt < 16; cnt++) { - if (data & mask) { - if (dllmin[cnt] > dlli) { - dllmin[cnt] = dlli; - } - if (dllmax[cnt] < dlli) { - dllmax[cnt] = dlli; - } - } - mask <<= 1; - } - passcnt++; - } else if (passcnt >= CBR_THRESHOLD) { - break; - } - } - data = 0; - for (cnt = 0; cnt < 8; cnt++) { - data >>= 3; - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) { - dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; - if (gold_sadj[0] >= dlli) { - dlli = (gold_sadj[0] - dlli) >> 1; - if (dlli > 3) { - dlli = 3; - } - } else { - dlli = (dlli - gold_sadj[0]) >> 1; - if (dlli > 4) { - dlli = 4; - } - dlli = (8 - dlli) & 0x7; - } - data |= dlli << 21; - } - } - moutdwm(ast, 0x1E6E0080, data); +static u32 cbr_scan3(struct ast_private *ast) +{ + u32 patcnt, loop; - data = 0; - for (cnt = 8; cnt < 16; cnt++) { - data >>= 3; - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) { - dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; - if (gold_sadj[1] >= dlli) { - dlli = (gold_sadj[1] - dlli) >> 1; - if (dlli > 3) { - dlli = 3; - } else { - dlli = (dlli - 1) & 0x7; - } - } else { - dlli = (dlli - gold_sadj[1]) >> 1; - dlli += 1; - if (dlli > 4) { - dlli = 4; - } - dlli = (8 - dlli) & 0x7; - } - data |= dlli << 21; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < 2; loop++) { + if (cbr_test3(ast)) + break; } + if (loop == 2) + return 0; } - moutdwm(ast, 0x1E6E0084, data); - -} /* finetuneDQI */ -#endif + return 1; +} -static void finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param) +static bool finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param) { - u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt; - + u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0; + bool status = false; FINETUNE_START: for (cnt = 0; cnt < 16; cnt++) { dllmin[cnt] = 0xff; @@ -690,16 +627,8 @@ FINETUNE_START: } passcnt = 0; for (dlli = 0; dlli < 76; dlli++) { - moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); - /* Wait DQSI latch phase calibration */ - moutdwm(ast, 0x1E6E0074, 0x00000010); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - - moutdwm(ast, 0x1E6E0074, CBR_SIZE1); + ast_moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE1); data = cbr_scan2(ast); if (data != 0) { mask = 0x00010001; @@ -727,9 +656,13 @@ FINETUNE_START: passcnt++; } } + if (retry++ > 10) + goto FINETUNE_DONE; if (passcnt != 16) { goto FINETUNE_START; } + status = true; +FINETUNE_DONE: gold_sadj[0] = gold_sadj[0] >> 4; gold_sadj[1] = gold_sadj[0]; @@ -753,7 +686,7 @@ FINETUNE_START: data |= dlli << 21; } } - moutdwm(ast, 0x1E6E0080, data); + ast_moutdwm(ast, 0x1E6E0080, data); data = 0; for (cnt = 8; cnt < 16; cnt++) { @@ -778,162 +711,116 @@ FINETUNE_START: data |= dlli << 21; } } - moutdwm(ast, 0x1E6E0084, data); - + ast_moutdwm(ast, 0x1E6E0084, data); + return status; } /* finetuneDQI_L */ -static void finetuneDQI_L2(struct ast_private *ast, struct ast2300_dram_param *param) +static void finetuneDQSI(struct ast_private *ast) { - u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, data2; + u32 dlli, dqsip, dqidly; + u32 reg_mcr18, reg_mcr0c, passcnt[2], diff; + u32 g_dqidly, g_dqsip, g_margin, g_side; + u16 pass[32][2][2]; + char tag[2][76]; + + /* Disable DQI CBR */ + reg_mcr0c = ast_mindwm(ast, 0x1E6E000C); + reg_mcr18 = ast_mindwm(ast, 0x1E6E0018); + reg_mcr18 &= 0x0000ffff; + ast_moutdwm(ast, 0x1E6E0018, reg_mcr18); - for (cnt = 0; cnt < 16; cnt++) { - dllmin[cnt] = 0xff; - dllmax[cnt] = 0x0; - } - passcnt = 0; for (dlli = 0; dlli < 76; dlli++) { - moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); - /* Wait DQSI latch phase calibration */ - moutdwm(ast, 0x1E6E0074, 0x00000010); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - - moutdwm(ast, 0x1E6E0074, CBR_SIZE2); - data = cbr_scan2(ast); - if (data != 0) { - mask = 0x00010001; - for (cnt = 0; cnt < 16; cnt++) { - if (data & mask) { - if (dllmin[cnt] > dlli) { - dllmin[cnt] = dlli; - } - if (dllmax[cnt] < dlli) { - dllmax[cnt] = dlli; - } - } - mask <<= 1; - } - passcnt++; - } else if (passcnt >= CBR_THRESHOLD2) { - break; - } + tag[0][dlli] = 0x0; + tag[1][dlli] = 0x0; } - gold_sadj[0] = 0x0; - gold_sadj[1] = 0xFF; - for (cnt = 0; cnt < 8; cnt++) { - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { - if (gold_sadj[0] < dllmin[cnt]) { - gold_sadj[0] = dllmin[cnt]; - } - if (gold_sadj[1] > dllmax[cnt]) { - gold_sadj[1] = dllmax[cnt]; - } - } + for (dqidly = 0; dqidly < 32; dqidly++) { + pass[dqidly][0][0] = 0xff; + pass[dqidly][0][1] = 0x0; + pass[dqidly][1][0] = 0xff; + pass[dqidly][1][1] = 0x0; } - gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1; - gold_sadj[1] = mindwm(ast, 0x1E6E0080); - - data = 0; - for (cnt = 0; cnt < 8; cnt++) { - data >>= 3; - data2 = gold_sadj[1] & 0x7; - gold_sadj[1] >>= 3; - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { - dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; - if (gold_sadj[0] >= dlli) { - dlli = (gold_sadj[0] - dlli) >> 1; - if (dlli > 0) { - dlli = 1; - } - if (data2 != 3) { - data2 = (data2 + dlli) & 0x7; - } - } else { - dlli = (dlli - gold_sadj[0]) >> 1; - if (dlli > 0) { - dlli = 1; - } - if (data2 != 4) { - data2 = (data2 - dlli) & 0x7; + for (dqidly = 0; dqidly < 32; dqidly++) { + passcnt[0] = passcnt[1] = 0; + for (dqsip = 0; dqsip < 2; dqsip++) { + ast_moutdwm(ast, 0x1E6E000C, 0); + ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23)); + ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c); + for (dlli = 0; dlli < 76; dlli++) { + ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0070, 0); + ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE0); + if (cbr_scan3(ast)) { + if (dlli == 0) + break; + passcnt[dqsip]++; + tag[dqsip][dlli] = 'P'; + if (dlli < pass[dqidly][dqsip][0]) + pass[dqidly][dqsip][0] = (u16) dlli; + if (dlli > pass[dqidly][dqsip][1]) + pass[dqidly][dqsip][1] = (u16) dlli; + } else if (passcnt[dqsip] >= 5) + break; + else { + pass[dqidly][dqsip][0] = 0xff; + pass[dqidly][dqsip][1] = 0x0; } } } - data |= data2 << 21; - } - moutdwm(ast, 0x1E6E0080, data); - - gold_sadj[0] = 0x0; - gold_sadj[1] = 0xFF; - for (cnt = 8; cnt < 16; cnt++) { - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { - if (gold_sadj[0] < dllmin[cnt]) { - gold_sadj[0] = dllmin[cnt]; - } - if (gold_sadj[1] > dllmax[cnt]) { - gold_sadj[1] = dllmax[cnt]; - } - } + if (passcnt[0] == 0 && passcnt[1] == 0) + dqidly++; } - gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1; - gold_sadj[1] = mindwm(ast, 0x1E6E0084); - - data = 0; - for (cnt = 8; cnt < 16; cnt++) { - data >>= 3; - data2 = gold_sadj[1] & 0x7; - gold_sadj[1] >>= 3; - if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) { - dlli = (dllmin[cnt] + dllmax[cnt]) >> 1; - if (gold_sadj[0] >= dlli) { - dlli = (gold_sadj[0] - dlli) >> 1; - if (dlli > 0) { - dlli = 1; - } - if (data2 != 3) { - data2 = (data2 + dlli) & 0x7; - } - } else { - dlli = (dlli - gold_sadj[0]) >> 1; - if (dlli > 0) { - dlli = 1; - } - if (data2 != 4) { - data2 = (data2 - dlli) & 0x7; - } + /* Search margin */ + g_dqidly = g_dqsip = g_margin = g_side = 0; + + for (dqidly = 0; dqidly < 32; dqidly++) { + for (dqsip = 0; dqsip < 2; dqsip++) { + if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1]) + continue; + diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0]; + if ((diff+2) < g_margin) + continue; + passcnt[0] = passcnt[1] = 0; + for (dlli = pass[dqidly][dqsip][0]; dlli > 0 && tag[dqsip][dlli] != 0; dlli--, passcnt[0]++); + for (dlli = pass[dqidly][dqsip][1]; dlli < 76 && tag[dqsip][dlli] != 0; dlli++, passcnt[1]++); + if (passcnt[0] > passcnt[1]) + passcnt[0] = passcnt[1]; + passcnt[1] = 0; + if (passcnt[0] > g_side) + passcnt[1] = passcnt[0] - g_side; + if (diff > (g_margin+1) && (passcnt[1] > 0 || passcnt[0] > 8)) { + g_margin = diff; + g_dqidly = dqidly; + g_dqsip = dqsip; + g_side = passcnt[0]; + } else if (passcnt[1] > 1 && g_side < 8) { + if (diff > g_margin) + g_margin = diff; + g_dqidly = dqidly; + g_dqsip = dqsip; + g_side = passcnt[0]; } } - data |= data2 << 21; } - moutdwm(ast, 0x1E6E0084, data); - -} /* finetuneDQI_L2 */ + reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23); + ast_moutdwm(ast, 0x1E6E0018, reg_mcr18); -static void cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param) +} +static bool cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param) { - u32 dllmin[2], dllmax[2], dlli, data, data2, passcnt; + u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0; + bool status = false; - - finetuneDQI_L(ast, param); - finetuneDQI_L2(ast, param); + finetuneDQSI(ast); + if (finetuneDQI_L(ast, param) == false) + return status; CBR_START2: dllmin[0] = dllmin[1] = 0xff; dllmax[0] = dllmax[1] = 0x0; passcnt = 0; for (dlli = 0; dlli < 76; dlli++) { - moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); - /* Wait DQSI latch phase calibration */ - moutdwm(ast, 0x1E6E0074, 0x00000010); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - - moutdwm(ast, 0x1E6E0074, CBR_SIZE2); + ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE2); data = cbr_scan(ast); if (data != 0) { if (data & 0x1) { @@ -957,44 +844,31 @@ CBR_START2: break; } } + if (retry++ > 10) + goto CBR_DONE2; if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) { goto CBR_START2; } if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) { goto CBR_START2; } + status = true; +CBR_DONE2: dlli = (dllmin[1] + dllmax[1]) >> 1; dlli <<= 8; dlli += (dllmin[0] + dllmax[0]) >> 1; - moutdwm(ast, 0x1E6E0068, (mindwm(ast, 0x1E6E0068) & 0xFFFF) | (dlli << 16)); - - data = (mindwm(ast, 0x1E6E0080) >> 24) & 0x1F; - data2 = (mindwm(ast, 0x1E6E0018) & 0xff80ffff) | (data << 16); - moutdwm(ast, 0x1E6E0018, data2); - moutdwm(ast, 0x1E6E0024, 0x8001 | (data << 1) | (param->dll2_finetune_step << 8)); - - /* Wait DQSI latch phase calibration */ - moutdwm(ast, 0x1E6E0074, 0x00000010); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - moutdwm(ast, 0x1E6E0070, 0x00000003); - do { - data = mindwm(ast, 0x1E6E0070); - } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16)); + return status; } /* CBRDLL2 */ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param) { u32 trap, trap_AC2, trap_MRS; - moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); /* Ger trap info */ - trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3; + trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3; trap_AC2 = 0x00020000 + (trap << 16); trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19); trap_MRS = 0x00000010 + (trap << 4); @@ -1008,22 +882,35 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa switch (param->dram_freq) { case 336: - moutdwm(ast, 0x1E6E2020, 0x0190); + ast_moutdwm(ast, 0x1E6E2020, 0x0190); param->wodt = 0; param->reg_AC1 = 0x22202725; param->reg_AC2 = 0xAA007613 | trap_AC2; param->reg_DQSIC = 0x000000BA; param->reg_MRS = 0x04001400 | trap_MRS; param->reg_EMRS = 0x00000000; - param->reg_IOZ = 0x00000034; + param->reg_IOZ = 0x00000023; param->reg_DQIDLY = 0x00000074; param->reg_FREQ = 0x00004DC0; param->madj_max = 96; param->dll2_finetune_step = 3; + switch (param->dram_chipid) { + default: + case AST_DRAM_512Mx16: + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xAA007613 | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xAA00761C | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xAA007636 | trap_AC2; + break; + } break; default: case 396: - moutdwm(ast, 0x1E6E2020, 0x03F1); + ast_moutdwm(ast, 0x1E6E2020, 0x03F1); param->wodt = 1; param->reg_AC1 = 0x33302825; param->reg_AC2 = 0xCC009617 | trap_AC2; @@ -1033,7 +920,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->reg_IOZ = 0x00000034; param->reg_DRV = 0x000000FA; param->reg_DQIDLY = 0x00000089; - param->reg_FREQ = 0x000050C0; + param->reg_FREQ = 0x00005040; param->madj_max = 96; param->dll2_finetune_step = 4; @@ -1053,14 +940,14 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa break; case 408: - moutdwm(ast, 0x1E6E2020, 0x01F0); + ast_moutdwm(ast, 0x1E6E2020, 0x01F0); param->wodt = 1; param->reg_AC1 = 0x33302825; param->reg_AC2 = 0xCC009617 | trap_AC2; param->reg_DQSIC = 0x000000E2; param->reg_MRS = 0x04001600 | trap_MRS; param->reg_EMRS = 0x00000000; - param->reg_IOZ = 0x00000034; + param->reg_IOZ = 0x00000023; param->reg_DRV = 0x000000FA; param->reg_DQIDLY = 0x00000089; param->reg_FREQ = 0x000050C0; @@ -1083,7 +970,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa break; case 456: - moutdwm(ast, 0x1E6E2020, 0x0230); + ast_moutdwm(ast, 0x1E6E2020, 0x0230); param->wodt = 0; param->reg_AC1 = 0x33302926; param->reg_AC2 = 0xCD44961A; @@ -1097,7 +984,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 4; break; case 504: - moutdwm(ast, 0x1E6E2020, 0x0270); + ast_moutdwm(ast, 0x1E6E2020, 0x0270); param->wodt = 1; param->reg_AC1 = 0x33302926; param->reg_AC2 = 0xDE44A61D; @@ -1111,7 +998,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 4; break; case 528: - moutdwm(ast, 0x1E6E2020, 0x0290); + ast_moutdwm(ast, 0x1E6E2020, 0x0290); param->wodt = 1; param->rodt = 1; param->reg_AC1 = 0x33302926; @@ -1127,7 +1014,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 576: - moutdwm(ast, 0x1E6E2020, 0x0140); + ast_moutdwm(ast, 0x1E6E2020, 0x0140); param->reg_MADJ = 0x00136868; param->reg_SADJ = 0x00004534; param->wodt = 1; @@ -1145,7 +1032,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 600: - moutdwm(ast, 0x1E6E2020, 0x02E1); + ast_moutdwm(ast, 0x1E6E2020, 0x02E1); param->reg_MADJ = 0x00136868; param->reg_SADJ = 0x00004534; param->wodt = 1; @@ -1163,7 +1050,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 624: - moutdwm(ast, 0x1E6E2020, 0x0160); + ast_moutdwm(ast, 0x1E6E2020, 0x0160); param->reg_MADJ = 0x00136868; param->reg_SADJ = 0x00004534; param->wodt = 1; @@ -1218,106 +1105,98 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param) { - u32 data, data2; + u32 data, data2, retry = 0; - moutdwm(ast, 0x1E6E0000, 0xFC600309); - moutdwm(ast, 0x1E6E0018, 0x00000100); - moutdwm(ast, 0x1E6E0024, 0x00000000); - moutdwm(ast, 0x1E6E0034, 0x00000000); +ddr3_init_start: + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0018, 0x00000100); + ast_moutdwm(ast, 0x1E6E0024, 0x00000000); + ast_moutdwm(ast, 0x1E6E0034, 0x00000000); udelay(10); - moutdwm(ast, 0x1E6E0064, param->reg_MADJ); - moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ); udelay(10); - moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); udelay(10); - moutdwm(ast, 0x1E6E0004, param->dram_config); - moutdwm(ast, 0x1E6E0008, 0x90040f); - moutdwm(ast, 0x1E6E0010, param->reg_AC1); - moutdwm(ast, 0x1E6E0014, param->reg_AC2); - moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); - moutdwm(ast, 0x1E6E0080, 0x00000000); - moutdwm(ast, 0x1E6E0084, 0x00000000); - moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); - moutdwm(ast, 0x1E6E0018, 0x4040A170); - moutdwm(ast, 0x1E6E0018, 0x20402370); - moutdwm(ast, 0x1E6E0038, 0x00000000); - moutdwm(ast, 0x1E6E0040, 0xFF444444); - moutdwm(ast, 0x1E6E0044, 0x22222222); - moutdwm(ast, 0x1E6E0048, 0x22222222); - moutdwm(ast, 0x1E6E004C, 0x00000002); - moutdwm(ast, 0x1E6E0050, 0x80000000); - moutdwm(ast, 0x1E6E0050, 0x00000000); - moutdwm(ast, 0x1E6E0054, 0); - moutdwm(ast, 0x1E6E0060, param->reg_DRV); - moutdwm(ast, 0x1E6E006C, param->reg_IOZ); - moutdwm(ast, 0x1E6E0070, 0x00000000); - moutdwm(ast, 0x1E6E0074, 0x00000000); - moutdwm(ast, 0x1E6E0078, 0x00000000); - moutdwm(ast, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0004, param->dram_config); + ast_moutdwm(ast, 0x1E6E0008, 0x90040f); + ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1); + ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2); + ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + ast_moutdwm(ast, 0x1E6E0080, 0x00000000); + ast_moutdwm(ast, 0x1E6E0084, 0x00000000); + ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + ast_moutdwm(ast, 0x1E6E0018, 0x4000A170); + ast_moutdwm(ast, 0x1E6E0018, 0x00002370); + ast_moutdwm(ast, 0x1E6E0038, 0x00000000); + ast_moutdwm(ast, 0x1E6E0040, 0xFF444444); + ast_moutdwm(ast, 0x1E6E0044, 0x22222222); + ast_moutdwm(ast, 0x1E6E0048, 0x22222222); + ast_moutdwm(ast, 0x1E6E004C, 0x00000002); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0054, 0); + ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV); + ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0074, 0x00000000); + ast_moutdwm(ast, 0x1E6E0078, 0x00000000); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); /* Wait MCLK2X lock to MCLK */ do { - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); } while (!(data & 0x08000000)); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00005C04); - udelay(10); - moutdwm(ast, 0x1E6E000C, 0x00000000); - moutdwm(ast, 0x1E6E0034, 0x00000000); - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); data = (data >> 8) & 0xff; while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { - data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; if ((data2 & 0xff) > param->madj_max) { break; } - moutdwm(ast, 0x1E6E0064, data2); + ast_moutdwm(ast, 0x1E6E0064, data2); if (data2 & 0x00100000) { data2 = ((data2 & 0xff) >> 3) + 3; } else { data2 = ((data2 & 0xff) >> 2) + 5; } - data = mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff; data2 += data & 0xff; data = data | (data2 << 8); - moutdwm(ast, 0x1E6E0068, data); + ast_moutdwm(ast, 0x1E6E0068, data); udelay(10); - moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000); + ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000); udelay(10); - data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff; - moutdwm(ast, 0x1E6E0018, data); + data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + ast_moutdwm(ast, 0x1E6E0018, data); data = data | 0x200; - moutdwm(ast, 0x1E6E0018, data); + ast_moutdwm(ast, 0x1E6E0018, data); do { - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); } while (!(data & 0x08000000)); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00005C04); - udelay(10); - moutdwm(ast, 0x1E6E000C, 0x00000000); - moutdwm(ast, 0x1E6E0034, 0x00000000); - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); data = (data >> 8) & 0xff; } - data = mindwm(ast, 0x1E6E0018) | 0xC00; - moutdwm(ast, 0x1E6E0018, data); + ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff); + data = ast_mindwm(ast, 0x1E6E0018) | 0xC00; + ast_moutdwm(ast, 0x1E6E0018, data); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00000040); + ast_moutdwm(ast, 0x1E6E0034, 0x00000001); + ast_moutdwm(ast, 0x1E6E000C, 0x00000040); udelay(50); /* Mode Register Setting */ - moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); - moutdwm(ast, 0x1E6E0030, param->reg_EMRS); - moutdwm(ast, 0x1E6E0028, 0x00000005); - moutdwm(ast, 0x1E6E0028, 0x00000007); - moutdwm(ast, 0x1E6E0028, 0x00000003); - moutdwm(ast, 0x1E6E0028, 0x00000001); - moutdwm(ast, 0x1E6E002C, param->reg_MRS); - moutdwm(ast, 0x1E6E000C, 0x00005C08); - moutdwm(ast, 0x1E6E0028, 0x00000001); - - moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000005); + ast_moutdwm(ast, 0x1E6E0028, 0x00000007); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS); + ast_moutdwm(ast, 0x1E6E000C, 0x00005C08); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + + ast_moutdwm(ast, 0x1E6E000C, 0x00005C01); data = 0; if (param->wodt) { data = 0x300; @@ -1325,30 +1204,23 @@ static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param) if (param->rodt) { data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); } - moutdwm(ast, 0x1E6E0034, data | 0x3); + ast_moutdwm(ast, 0x1E6E0034, data | 0x3); - /* Wait DQI delay lock */ - do { - data = mindwm(ast, 0x1E6E0080); - } while (!(data & 0x40000000)); - /* Wait DQSI delay lock */ - do { - data = mindwm(ast, 0x1E6E0020); - } while (!(data & 0x00000800)); /* Calibrate the DQSI delay */ - cbr_dll2(ast, param); + if ((cbr_dll2(ast, param) == false) && (retry++ < 10)) + goto ddr3_init_start; - moutdwm(ast, 0x1E6E0120, param->reg_FREQ); + ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ); /* ECC Memory Initialization */ #ifdef ECC - moutdwm(ast, 0x1E6E007C, 0x00000000); - moutdwm(ast, 0x1E6E0070, 0x221); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x221); do { - data = mindwm(ast, 0x1E6E0070); + data = ast_mindwm(ast, 0x1E6E0070); } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - moutdwm(ast, 0x1E6E0050, 0x80000000); - moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); #endif @@ -1358,10 +1230,10 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa { u32 trap, trap_AC2, trap_MRS; - moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); /* Ger trap info */ - trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3; + trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3; trap_AC2 = (trap << 20) | (trap << 16); trap_AC2 += 0x00110000; trap_MRS = 0x00000040 | (trap << 4); @@ -1375,7 +1247,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa switch (param->dram_freq) { case 264: - moutdwm(ast, 0x1E6E2020, 0x0130); + ast_moutdwm(ast, 0x1E6E2020, 0x0130); param->wodt = 0; param->reg_AC1 = 0x11101513; param->reg_AC2 = 0x78117011; @@ -1390,7 +1262,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 336: - moutdwm(ast, 0x1E6E2020, 0x0190); + ast_moutdwm(ast, 0x1E6E2020, 0x0190); param->wodt = 1; param->reg_AC1 = 0x22202613; param->reg_AC2 = 0xAA009016 | trap_AC2; @@ -1403,10 +1275,25 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->reg_FREQ = 0x00004DC0; param->madj_max = 96; param->dll2_finetune_step = 3; + switch (param->dram_chipid) { + default: + case AST_DRAM_512Mx16: + param->reg_AC2 = 0xAA009012 | trap_AC2; + break; + case AST_DRAM_1Gx16: + param->reg_AC2 = 0xAA009016 | trap_AC2; + break; + case AST_DRAM_2Gx16: + param->reg_AC2 = 0xAA009023 | trap_AC2; + break; + case AST_DRAM_4Gx16: + param->reg_AC2 = 0xAA00903B | trap_AC2; + break; + } break; default: case 396: - moutdwm(ast, 0x1E6E2020, 0x03F1); + ast_moutdwm(ast, 0x1E6E2020, 0x03F1); param->wodt = 1; param->rodt = 0; param->reg_AC1 = 0x33302714; @@ -1417,7 +1304,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->reg_DRV = 0x000000FA; param->reg_IOZ = 0x00000034; param->reg_DQIDLY = 0x00000089; - param->reg_FREQ = 0x000050C0; + param->reg_FREQ = 0x00005040; param->madj_max = 96; param->dll2_finetune_step = 4; @@ -1440,7 +1327,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa break; case 408: - moutdwm(ast, 0x1E6E2020, 0x01F0); + ast_moutdwm(ast, 0x1E6E2020, 0x01F0); param->wodt = 1; param->rodt = 0; param->reg_AC1 = 0x33302714; @@ -1473,7 +1360,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa break; case 456: - moutdwm(ast, 0x1E6E2020, 0x0230); + ast_moutdwm(ast, 0x1E6E2020, 0x0230); param->wodt = 0; param->reg_AC1 = 0x33302815; param->reg_AC2 = 0xCD44B01E; @@ -1488,7 +1375,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 504: - moutdwm(ast, 0x1E6E2020, 0x0261); + ast_moutdwm(ast, 0x1E6E2020, 0x0261); param->wodt = 1; param->rodt = 1; param->reg_AC1 = 0x33302815; @@ -1504,7 +1391,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 528: - moutdwm(ast, 0x1E6E2020, 0x0120); + ast_moutdwm(ast, 0x1E6E2020, 0x0120); param->wodt = 1; param->rodt = 1; param->reg_AC1 = 0x33302815; @@ -1520,7 +1407,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 552: - moutdwm(ast, 0x1E6E2020, 0x02A1); + ast_moutdwm(ast, 0x1E6E2020, 0x02A1); param->wodt = 1; param->rodt = 1; param->reg_AC1 = 0x43402915; @@ -1536,7 +1423,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa param->dll2_finetune_step = 3; break; case 576: - moutdwm(ast, 0x1E6E2020, 0x0140); + ast_moutdwm(ast, 0x1E6E2020, 0x0140); param->wodt = 1; param->rodt = 1; param->reg_AC1 = 0x43402915; @@ -1588,110 +1475,102 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param) { - u32 data, data2; - - moutdwm(ast, 0x1E6E0000, 0xFC600309); - moutdwm(ast, 0x1E6E0018, 0x00000100); - moutdwm(ast, 0x1E6E0024, 0x00000000); - moutdwm(ast, 0x1E6E0064, param->reg_MADJ); - moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + u32 data, data2, retry = 0; + +ddr2_init_start: + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0018, 0x00000100); + ast_moutdwm(ast, 0x1E6E0024, 0x00000000); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ); udelay(10); - moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); udelay(10); - moutdwm(ast, 0x1E6E0004, param->dram_config); - moutdwm(ast, 0x1E6E0008, 0x90040f); - moutdwm(ast, 0x1E6E0010, param->reg_AC1); - moutdwm(ast, 0x1E6E0014, param->reg_AC2); - moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); - moutdwm(ast, 0x1E6E0080, 0x00000000); - moutdwm(ast, 0x1E6E0084, 0x00000000); - moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); - moutdwm(ast, 0x1E6E0018, 0x4040A130); - moutdwm(ast, 0x1E6E0018, 0x20402330); - moutdwm(ast, 0x1E6E0038, 0x00000000); - moutdwm(ast, 0x1E6E0040, 0xFF808000); - moutdwm(ast, 0x1E6E0044, 0x88848466); - moutdwm(ast, 0x1E6E0048, 0x44440008); - moutdwm(ast, 0x1E6E004C, 0x00000000); - moutdwm(ast, 0x1E6E0050, 0x80000000); - moutdwm(ast, 0x1E6E0050, 0x00000000); - moutdwm(ast, 0x1E6E0054, 0); - moutdwm(ast, 0x1E6E0060, param->reg_DRV); - moutdwm(ast, 0x1E6E006C, param->reg_IOZ); - moutdwm(ast, 0x1E6E0070, 0x00000000); - moutdwm(ast, 0x1E6E0074, 0x00000000); - moutdwm(ast, 0x1E6E0078, 0x00000000); - moutdwm(ast, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0004, param->dram_config); + ast_moutdwm(ast, 0x1E6E0008, 0x90040f); + ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1); + ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2); + ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + ast_moutdwm(ast, 0x1E6E0080, 0x00000000); + ast_moutdwm(ast, 0x1E6E0084, 0x00000000); + ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + ast_moutdwm(ast, 0x1E6E0018, 0x4000A130); + ast_moutdwm(ast, 0x1E6E0018, 0x00002330); + ast_moutdwm(ast, 0x1E6E0038, 0x00000000); + ast_moutdwm(ast, 0x1E6E0040, 0xFF808000); + ast_moutdwm(ast, 0x1E6E0044, 0x88848466); + ast_moutdwm(ast, 0x1E6E0048, 0x44440008); + ast_moutdwm(ast, 0x1E6E004C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0054, 0); + ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV); + ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0074, 0x00000000); + ast_moutdwm(ast, 0x1E6E0078, 0x00000000); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); /* Wait MCLK2X lock to MCLK */ do { - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); } while (!(data & 0x08000000)); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00005C04); - udelay(10); - moutdwm(ast, 0x1E6E000C, 0x00000000); - moutdwm(ast, 0x1E6E0034, 0x00000000); - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); data = (data >> 8) & 0xff; while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { - data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; if ((data2 & 0xff) > param->madj_max) { break; } - moutdwm(ast, 0x1E6E0064, data2); + ast_moutdwm(ast, 0x1E6E0064, data2); if (data2 & 0x00100000) { data2 = ((data2 & 0xff) >> 3) + 3; } else { data2 = ((data2 & 0xff) >> 2) + 5; } - data = mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff; data2 += data & 0xff; data = data | (data2 << 8); - moutdwm(ast, 0x1E6E0068, data); + ast_moutdwm(ast, 0x1E6E0068, data); udelay(10); - moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000); + ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000); udelay(10); - data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff; - moutdwm(ast, 0x1E6E0018, data); + data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + ast_moutdwm(ast, 0x1E6E0018, data); data = data | 0x200; - moutdwm(ast, 0x1E6E0018, data); + ast_moutdwm(ast, 0x1E6E0018, data); do { - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); } while (!(data & 0x08000000)); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00005C04); - udelay(10); - moutdwm(ast, 0x1E6E000C, 0x00000000); - moutdwm(ast, 0x1E6E0034, 0x00000000); - data = mindwm(ast, 0x1E6E001C); + data = ast_mindwm(ast, 0x1E6E001C); data = (data >> 8) & 0xff; } - data = mindwm(ast, 0x1E6E0018) | 0xC00; - moutdwm(ast, 0x1E6E0018, data); + ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff); + data = ast_mindwm(ast, 0x1E6E0018) | 0xC00; + ast_moutdwm(ast, 0x1E6E0018, data); - moutdwm(ast, 0x1E6E0034, 0x00000001); - moutdwm(ast, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0034, 0x00000001); + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); udelay(50); /* Mode Register Setting */ - moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); - moutdwm(ast, 0x1E6E0030, param->reg_EMRS); - moutdwm(ast, 0x1E6E0028, 0x00000005); - moutdwm(ast, 0x1E6E0028, 0x00000007); - moutdwm(ast, 0x1E6E0028, 0x00000003); - moutdwm(ast, 0x1E6E0028, 0x00000001); - - moutdwm(ast, 0x1E6E000C, 0x00005C08); - moutdwm(ast, 0x1E6E002C, param->reg_MRS); - moutdwm(ast, 0x1E6E0028, 0x00000001); - moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380); - moutdwm(ast, 0x1E6E0028, 0x00000003); - moutdwm(ast, 0x1E6E0030, param->reg_EMRS); - moutdwm(ast, 0x1E6E0028, 0x00000003); - - moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000005); + ast_moutdwm(ast, 0x1E6E0028, 0x00000007); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + + ast_moutdwm(ast, 0x1E6E000C, 0x00005C08); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + + ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); data = 0; if (param->wodt) { data = 0x500; @@ -1699,30 +1578,23 @@ static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param) if (param->rodt) { data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); } - moutdwm(ast, 0x1E6E0034, data | 0x3); - moutdwm(ast, 0x1E6E0120, param->reg_FREQ); + ast_moutdwm(ast, 0x1E6E0034, data | 0x3); + ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ); - /* Wait DQI delay lock */ - do { - data = mindwm(ast, 0x1E6E0080); - } while (!(data & 0x40000000)); - /* Wait DQSI delay lock */ - do { - data = mindwm(ast, 0x1E6E0020); - } while (!(data & 0x00000800)); /* Calibrate the DQSI delay */ - cbr_dll2(ast, param); + if ((cbr_dll2(ast, param) == false) && (retry++ < 10)) + goto ddr2_init_start; /* ECC Memory Initialization */ #ifdef ECC - moutdwm(ast, 0x1E6E007C, 0x00000000); - moutdwm(ast, 0x1E6E0070, 0x221); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x221); do { - data = mindwm(ast, 0x1E6E0070); + data = ast_mindwm(ast, 0x1E6E0070); } while (!(data & 0x00001000)); - moutdwm(ast, 0x1E6E0070, 0x00000000); - moutdwm(ast, 0x1E6E0050, 0x80000000); - moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); #endif } @@ -1768,8 +1640,8 @@ static void ast_init_dram_2300(struct drm_device *dev) ddr2_init(ast, ¶m); } - temp = mindwm(ast, 0x1e6e2040); - moutdwm(ast, 0x1e6e2040, temp | 0x40); + temp = ast_mindwm(ast, 0x1e6e2040); + ast_moutdwm(ast, 0x1e6e2040, temp | 0x40); } /* wait ready */ diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h index 95fa6aba26b..4c761dcea97 100644 --- a/drivers/gpu/drm/ast/ast_tables.h +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -42,7 +42,7 @@ #define HBorder 0x00000020 #define VBorder 0x00000010 #define WideScreenMode 0x00000100 - +#define NewModeInfo 0x00000200 /* DCLK Index */ #define VCLK25_175 0x00 @@ -67,6 +67,11 @@ #define VCLK106_5 0x12 #define VCLK146_25 0x13 #define VCLK148_5 0x14 +#define VCLK71 0x15 +#define VCLK88_75 0x16 +#define VCLK119 0x17 +#define VCLK85_5 0x18 +#define VCLK97_75 0x19 static struct ast_vbios_dclk_info dclk_table[] = { {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ @@ -90,6 +95,10 @@ static struct ast_vbios_dclk_info dclk_table[] = { {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x77, 0x58, 0x80}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ }; static struct ast_vbios_stdtable vbios_stdtable[] = { @@ -225,41 +234,63 @@ static struct ast_vbios_enhtable res_1600x1200[] = { (SyncPP | Charx8Dot), 0xFF, 1, 0x33 }, }; -static struct ast_vbios_enhtable res_1920x1200[] = { - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ - (SyncNP | Charx8Dot), 60, 1, 0x34 }, - {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ - (SyncNP | Charx8Dot), 0xFF, 1, 0x34 }, +/* 16:9 */ +static struct ast_vbios_enhtable res_1360x768[] = { + {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 }, + {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* end */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x39 }, +}; + +static struct ast_vbios_enhtable res_1600x900[] = { + {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A }, + {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* end */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x3A } }; +static struct ast_vbios_enhtable res_1920x1080[] = { + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x38 }, + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x38 }, +}; + + /* 16:10 */ static struct ast_vbios_enhtable res_1280x800[] = { + {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x35 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 }, {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x35 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x35 }, }; static struct ast_vbios_enhtable res_1440x900[] = { + {1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x36 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 }, {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x36 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x36 }, }; static struct ast_vbios_enhtable res_1680x1050[] = { + {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x37 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 }, {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ - (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x37 }, + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x37 }, }; -/* HDTV */ -static struct ast_vbios_enhtable res_1920x1080[] = { - {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x38 }, - {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ - (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x38 }, +static struct ast_vbios_enhtable res_1920x1200[] = { + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 }, + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 }, }; + #endif diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index b171901a355..98fd17ae491 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -225,12 +225,6 @@ out: return num_modes; } -static int ptn3460_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge; @@ -242,7 +236,6 @@ struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { .get_modes = ptn3460_get_modes, - .mode_valid = ptn3460_mode_valid, .best_encoder = ptn3460_best_encoder, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index f59433b7610..49332c5fe35 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -505,13 +505,6 @@ static int cirrus_vga_get_modes(struct drm_connector *connector) return count; } -static int cirrus_vga_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - /* Any mode we've added is valid */ - return MODE_OK; -} - static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector *connector) { @@ -546,7 +539,6 @@ static void cirrus_connector_destroy(struct drm_connector *connector) struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = { .get_modes = cirrus_vga_get_modes, - .mode_valid = cirrus_vga_mode_valid, .best_encoder = cirrus_connector_best_encoder, }; diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index ef7f0199a0f..68175b54504 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -363,7 +363,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, list->master = dev->primary->master; *maplist = list; return 0; - } +} int drm_addmap(struct drm_device * dev, resource_size_t offset, unsigned int size, enum drm_map_type type, diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d8b7099abec..34f0bf18d80 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1145,16 +1145,19 @@ EXPORT_SYMBOL(drm_plane_cleanup); */ void drm_plane_force_disable(struct drm_plane *plane) { + struct drm_framebuffer *old_fb = plane->fb; int ret; - if (!plane->fb) + if (!old_fb) return; ret = plane->funcs->disable_plane(plane); - if (ret) + if (ret) { DRM_ERROR("failed to disable plane with busy fb\n"); + return; + } /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->fb); + __drm_framebuffer_unreference(old_fb); plane->fb = NULL; plane->crtc = NULL; } @@ -1378,6 +1381,12 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr return 0; } +void drm_mode_group_destroy(struct drm_mode_group *group) +{ + kfree(group->id_list); + group->id_list = NULL; +} + /* * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is * the drm core's responsibility to set up mode control groups. @@ -2116,9 +2125,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!plane_req->fb_id) { drm_modeset_lock_all(dev); old_fb = plane->fb; - plane->funcs->disable_plane(plane); - plane->crtc = NULL; - plane->fb = NULL; + ret = plane->funcs->disable_plane(plane); + if (!ret) { + plane->crtc = NULL; + plane->fb = NULL; + } else { + old_fb = NULL; + } drm_modeset_unlock_all(dev); goto out; } @@ -2187,16 +2200,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } drm_modeset_lock_all(dev); + old_fb = plane->fb; ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { - old_fb = plane->fb; plane->crtc = crtc; plane->fb = fb; fb = NULL; + } else { + old_fb = NULL; } drm_modeset_unlock_all(dev); @@ -2239,9 +2254,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) ret = crtc->funcs->set_config(set); if (ret == 0) { crtc->primary->crtc = crtc; - - /* crtc->fb must be updated by ->set_config, enforces this. */ - WARN_ON(fb != crtc->primary->fb); + crtc->primary->fb = fb; } list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index df281b54db0..a8b78e7bde5 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -140,16 +140,10 @@ drm_encoder_disable(struct drm_encoder *encoder) static void __drm_helper_disable_unused_functions(struct drm_device *dev) { struct drm_encoder *encoder; - struct drm_connector *connector; struct drm_crtc *crtc; drm_warn_on_modeset_not_all_locked(dev); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (!connector->encoder) - continue; - } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (!drm_helper_encoder_in_use(encoder)) { drm_encoder_disable(encoder); @@ -387,8 +381,7 @@ done: } EXPORT_SYMBOL(drm_crtc_helper_set_mode); - -static int +static void drm_crtc_helper_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -417,7 +410,6 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) } __drm_helper_disable_unused_functions(dev); - return 0; } /** @@ -468,7 +460,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) (int)set->num_connectors, set->x, set->y); } else { DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); - return drm_crtc_helper_disable(set->crtc); + drm_crtc_helper_disable(set->crtc); + return 0; } dev = set->crtc->dev; diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4b6e6f3ba0a..a13f1f51f68 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -206,7 +206,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper * @adapter: i2c adapter to register * - * This registers an i2c adapater that uses dp aux channel as it's underlaying + * This registers an i2c adapter that uses dp aux channel as it's underlaying * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure * and store it in the algo_data member of the @adapter argument. This will be * used by the i2c over dp aux algorithm to drive the hardware. diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d4e3f9d9370..7a4fd2ed128 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -984,9 +984,13 @@ static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; - /* - * Sanity check the header of the base EDID block. Return 8 if the header - * is perfect, down to 0 if it's totally wrong. +/** + * drm_edid_header_is_valid - sanity check the header of the base EDID block + * @raw_edid: pointer to raw base EDID block + * + * Sanity check the header of the base EDID block. + * + * Return: 8 if the header is perfect, down to 0 if it's totally wrong. */ int drm_edid_header_is_valid(const u8 *raw_edid) { @@ -1005,9 +1009,16 @@ module_param_named(edid_fixup, edid_fixup, int, 0400); MODULE_PARM_DESC(edid_fixup, "Minimum number of valid EDID header bytes (0-8, default 6)"); -/* - * Sanity check the EDID block (base or extension). Return 0 if the block - * doesn't check out, or 1 if it's valid. +/** + * drm_edid_block_valid - Sanity check the EDID block (base or extension) + * @raw_edid: pointer to raw EDID block + * @block: type of block to validate (0 for base, extension otherwise) + * @print_bad_edid: if true, dump bad EDID blocks to the console + * + * Validate a base or extension EDID block and optionally dump bad blocks to + * the console. + * + * Return: True if the block is valid, false otherwise. */ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) { @@ -1077,6 +1088,8 @@ EXPORT_SYMBOL(drm_edid_block_valid); * @edid: EDID data * * Sanity-check an entire EDID record (including extensions) + * + * Return: True if the EDID data is valid, false otherwise. */ bool drm_edid_is_valid(struct edid *edid) { @@ -1096,18 +1109,15 @@ EXPORT_SYMBOL(drm_edid_is_valid); #define DDC_SEGMENT_ADDR 0x30 /** - * Get EDID information via I2C. - * - * @adapter : i2c device adaptor + * drm_do_probe_ddc_edid() - get EDID information via I2C + * @adapter: I2C device adaptor * @buf: EDID data buffer to be filled * @block: 128 byte EDID block to start fetching from * @len: EDID data buffer length to fetch * - * Returns: - * - * 0 on success or -1 on failure. + * Try to fetch EDID information by calling I2C driver functions. * - * Try to fetch EDID information by calling i2c driver function. + * Return: 0 on success or -1 on failure. */ static int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, @@ -1118,7 +1128,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, unsigned char xfers = segment ? 3 : 2; int ret, retries = 5; - /* The core i2c driver will automatically retry the transfer if the + /* + * The core I2C driver will automatically retry the transfer if the * adapter reports EAGAIN. However, we find that bit-banging transfers * are susceptible to errors under a heavily loaded machine and * generate spurious NAKs and timeouts. Retrying the transfer @@ -1144,10 +1155,10 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, } }; - /* - * Avoid sending the segment addr to not upset non-compliant ddc - * monitors. - */ + /* + * Avoid sending the segment addr to not upset non-compliant + * DDC monitors. + */ ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers); if (ret == -ENXIO) { @@ -1246,12 +1257,10 @@ out: } /** - * Probe DDC presence. - * @adapter: i2c adapter to probe + * drm_probe_ddc() - probe DDC presence + * @adapter: I2C adapter to probe * - * Returns: - * - * 1 on success + * Return: True on success, false on failure. */ bool drm_probe_ddc(struct i2c_adapter *adapter) @@ -1265,12 +1274,12 @@ EXPORT_SYMBOL(drm_probe_ddc); /** * drm_get_edid - get EDID data, if available * @connector: connector we're probing - * @adapter: i2c adapter to use for DDC + * @adapter: I2C adapter to use for DDC * - * Poke the given i2c channel to grab EDID data if possible. If found, + * Poke the given I2C channel to grab EDID data if possible. If found, * attach it to the connector. * - * Return edid data or NULL if we couldn't find any. + * Return: Pointer to valid EDID or NULL if we couldn't find any. */ struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) @@ -1288,7 +1297,7 @@ EXPORT_SYMBOL(drm_get_edid); * drm_edid_duplicate - duplicate an EDID and the extensions * @edid: EDID to duplicate * - * Return duplicate edid or NULL on allocation failure. + * Return: Pointer to duplicated EDID or NULL on allocation failure. */ struct edid *drm_edid_duplicate(const struct edid *edid) { @@ -1411,7 +1420,8 @@ mode_is_rb(const struct drm_display_mode *mode) * @rb: Mode reduced-blanking-ness * * Walk the DMT mode list looking for a match for the given parameters. - * Return a newly allocated copy of the mode, or NULL if not found. + * + * Return: A newly allocated copy of the mode, or NULL if not found. */ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, @@ -1595,14 +1605,13 @@ bad_std_timing(u8 a, u8 b) * @connector: connector of for the EDID block * @edid: EDID block to scan * @t: standard timing params - * @revision: standard timing level * * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT/GTF/DMT. */ static struct drm_display_mode * drm_mode_std(struct drm_connector *connector, struct edid *edid, - struct std_timing *t, int revision) + struct std_timing *t) { struct drm_device *dev = connector->dev; struct drm_display_mode *m, *mode = NULL; @@ -1623,7 +1632,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid, vrefresh_rate = vfreq + 60; /* the vdisplay is calculated based on the aspect ratio */ if (aspect_ratio == 0) { - if (revision < 3) + if (edid->revision < 3) vsize = hsize; else vsize = (hsize * 10) / 16; @@ -2140,7 +2149,7 @@ do_established_modes(struct detailed_timing *timing, void *c) /** * add_established_modes - get est. modes from EDID and add them - * @connector: connector of for the EDID block + * @connector: connector to add mode(s) to * @edid: EDID block to scan * * Each EDID block contains a bitmap of the supported "established modes" list @@ -2191,8 +2200,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) struct drm_display_mode *newmode; std = &data->data.timings[i]; - newmode = drm_mode_std(connector, edid, std, - edid->revision); + newmode = drm_mode_std(connector, edid, std); if (newmode) { drm_mode_probed_add(connector, newmode); closure->modes++; @@ -2203,7 +2211,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) /** * add_standard_modes - get std. modes from EDID and add them - * @connector: connector of for the EDID block + * @connector: connector to add mode(s) to * @edid: EDID block to scan * * Standard modes can be calculated using the appropriate standard (DMT, @@ -2221,8 +2229,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid) struct drm_display_mode *newmode; newmode = drm_mode_std(connector, edid, - &edid->standard_timings[i], - edid->revision); + &edid->standard_timings[i]); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; @@ -2425,7 +2432,7 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) * drm_match_cea_mode - look for a CEA mode matching given mode * @to_match: display mode * - * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 + * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 * mode. */ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) @@ -2452,6 +2459,22 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) } EXPORT_SYMBOL(drm_match_cea_mode); +/** + * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to + * the input VIC from the CEA mode list + * @video_code: ID given to each of the CEA modes + * + * Returns picture aspect ratio + */ +enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) +{ + /* return picture aspect ratio for video_code - 1 to access the + * right array element + */ + return edid_cea_modes[video_code-1].picture_aspect_ratio; +} +EXPORT_SYMBOL(drm_get_cea_aspect_ratio); + /* * Calculate the alternate clock for HDMI modes (those from the HDMI vendor * specific block). @@ -3023,11 +3046,9 @@ monitor_name(struct detailed_timing *t, void *data) * @connector: connector corresponding to the HDMI/DP sink * @edid: EDID to parse * - * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. - * Some ELD fields are left to the graphics driver caller: - * - Conn_Type - * - HDCP - * - Port_ID + * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The + * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to + * fill in. */ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) { @@ -3111,9 +3132,10 @@ EXPORT_SYMBOL(drm_edid_to_eld); * @sads: pointer that will be set to the extracted SADs * * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. - * Note: returned pointer needs to be kfreed * - * Return number of found SADs or negative number on error. + * Note: The returned pointer needs to be freed using kfree(). + * + * Return: The number of found SADs or negative number on error. */ int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) { @@ -3170,9 +3192,11 @@ EXPORT_SYMBOL(drm_edid_to_sad); * @sadb: pointer to the speaker block * * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. - * Note: returned pointer needs to be kfreed * - * Return number of found Speaker Allocation Blocks or negative number on error. + * Note: The returned pointer needs to be freed using kfree(). + * + * Return: The number of found Speaker Allocation Blocks or negative number on + * error. */ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) { @@ -3219,9 +3243,12 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb) EXPORT_SYMBOL(drm_edid_to_speaker_allocation); /** - * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond + * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay * @connector: connector associated with the HDMI/DP sink * @mode: the display mode + * + * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if + * the sink doesn't support audio or video. */ int drm_av_sync_delay(struct drm_connector *connector, struct drm_display_mode *mode) @@ -3263,6 +3290,9 @@ EXPORT_SYMBOL(drm_av_sync_delay); * * It's possible for one encoder to be associated with multiple HDMI/DP sinks. * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. + * + * Return: The connector associated with the first HDMI/DP sink that has ELD + * attached to it. */ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, struct drm_display_mode *mode) @@ -3279,11 +3309,12 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, EXPORT_SYMBOL(drm_select_eld); /** - * drm_detect_hdmi_monitor - detect whether monitor is hdmi. + * drm_detect_hdmi_monitor - detect whether monitor is HDMI * @edid: monitor EDID information * * Parse the CEA extension according to CEA-861-B. - * Return true if HDMI, false if not or unknown. + * + * Return: True if the monitor is HDMI, false if not or unknown. */ bool drm_detect_hdmi_monitor(struct edid *edid) { @@ -3321,6 +3352,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); * audio format, assume at least 'basic audio' support, even if 'basic * audio' is not defined in EDID. * + * Return: True if the monitor supports audio, false otherwise. */ bool drm_detect_monitor_audio(struct edid *edid) { @@ -3364,6 +3396,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio); * Check whether the monitor reports the RGB quantization range selection * as supported. The AVI infoframe can then be used to inform the monitor * which quantization range (full or limited) is used. + * + * Return: True if the RGB quantization range is selectable, false otherwise. */ bool drm_rgb_quant_range_selectable(struct edid *edid) { @@ -3468,11 +3502,11 @@ static void drm_add_display_info(struct edid *edid, /** * drm_add_edid_modes - add modes from EDID data, if available * @connector: connector we're probing - * @edid: edid data + * @edid: EDID data * * Add the specified modes to the connector's mode list. * - * Return number of modes added or 0 if we couldn't find any. + * Return: The number of modes added or 0 if we couldn't find any. */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { @@ -3534,7 +3568,7 @@ EXPORT_SYMBOL(drm_add_edid_modes); * Add the specified modes to the connector's mode list. Only when the * hdisplay/vdisplay is not beyond the given limit, it will be added. * - * Return number of modes added or 0 if we couldn't find any. + * Return: The number of modes added or 0 if we couldn't find any. */ int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay) @@ -3573,13 +3607,22 @@ int drm_add_modes_noedid(struct drm_connector *connector, } EXPORT_SYMBOL(drm_add_modes_noedid); +/** + * drm_set_preferred_mode - Sets the preferred mode of a connector + * @connector: connector whose mode list should be processed + * @hpref: horizontal resolution of preferred mode + * @vpref: vertical resolution of preferred mode + * + * Marks a mode as preferred if it matches the resolution specified by @hpref + * and @vpref. + */ void drm_set_preferred_mode(struct drm_connector *connector, int hpref, int vpref) { struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (mode->hdisplay == hpref && + if (mode->hdisplay == hpref && mode->vdisplay == vpref) mode->type |= DRM_MODE_TYPE_PREFERRED; } @@ -3592,7 +3635,7 @@ EXPORT_SYMBOL(drm_set_preferred_mode); * @frame: HDMI AVI infoframe * @mode: DRM display mode * - * Returns 0 on success or a negative error code on failure. + * Return: 0 on success or a negative error code on failure. */ int drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, @@ -3613,6 +3656,12 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, frame->video_code = drm_match_cea_mode(mode); frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; + + /* Populate picture aspect ratio from CEA mode list */ + if (frame->video_code > 0) + frame->picture_aspect = drm_get_cea_aspect_ratio( + frame->video_code); + frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; @@ -3657,7 +3706,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode) * 4k or stereoscopic 3D mode. So when giving any other mode as input this * function will return -EINVAL, error that can be safely ignored. * - * Returns 0 on success or a negative error code on failure. + * Return: 0 on success or a negative error code on failure. */ int drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 04d3fd3658f..e95ed5805f0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -45,13 +45,13 @@ static LIST_HEAD(kernel_fb_helper_list); * DOC: fbdev helpers * * The fb helper functions are useful to provide an fbdev on top of a drm kernel - * mode setting driver. They can be used mostly independantely from the crtc + * mode setting driver. They can be used mostly independently from the crtc * helper functions used by many drivers to implement the kernel mode setting * interfaces. * * Initialization is done as a three-step process with drm_fb_helper_init(), * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). - * Drivers with fancier requirements than the default beheviour can override the + * Drivers with fancier requirements than the default behaviour can override the * second step with their own code. Teardown is done with drm_fb_helper_fini(). * * At runtime drivers should restore the fbdev console by calling @@ -59,7 +59,7 @@ static LIST_HEAD(kernel_fb_helper_list); * should also notify the fb helper code from updates to the output * configuration by calling drm_fb_helper_hotplug_event(). For easier * integration with the output polling code in drm_crtc_helper.c the modeset - * code proves a ->output_poll_changed callback. + * code provides a ->output_poll_changed callback. * * All other functions exported by the fb helper library can be used to * implement the fbdev driver interface by the driver. @@ -326,12 +326,21 @@ static bool drm_fb_helper_force_kernel_mode(void) return false; list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { - if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) + struct drm_device *dev = helper->dev; + + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + continue; + + if (!mutex_trylock(&dev->mode_config.mutex)) { + error = true; continue; + } ret = drm_fb_helper_restore_fbdev_mode(helper); if (ret) error = true; + + mutex_unlock(&dev->mode_config.mutex); } return error; } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index e1eba0b7cd4..021fe5d11df 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -43,8 +43,7 @@ DEFINE_MUTEX(drm_global_mutex); EXPORT_SYMBOL(drm_global_mutex); -static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_minor *minor); +static int drm_open_helper(struct file *filp, struct drm_minor *minor); static int drm_setup(struct drm_device * dev) { @@ -95,7 +94,7 @@ int drm_open(struct inode *inode, struct file *filp) /* share address_space across all char-devs of a single device */ filp->f_mapping = dev->anon_inode->i_mapping; - retcode = drm_open_helper(inode, filp, minor); + retcode = drm_open_helper(filp, minor); if (retcode) goto err_undo; if (need_setup) { @@ -171,7 +170,6 @@ static int drm_cpu_valid(void) /** * Called whenever a process opens /dev/drm. * - * \param inode device inode. * \param filp file pointer. * \param minor acquired minor-object. * \return zero on success or a negative number on failure. @@ -179,8 +177,7 @@ static int drm_cpu_valid(void) * Creates and initializes a drm_file structure for the file private data in \p * filp and add it into the double linked list in \p dev. */ -static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_minor *minor) +static int drm_open_helper(struct file *filp, struct drm_minor *minor) { struct drm_device *dev = minor->dev; struct drm_file *priv; diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 7583767ec61..5ba9a614e7a 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1,6 +1,5 @@ -/** - * \file drm_irq.c - * IRQ support +/* + * drm_irq.c IRQ and vblank support * * \author Rickard E. (Rik) Faith <faith@valinux.com> * \author Gareth Hughes <gareth@valinux.com> @@ -140,33 +139,40 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) static void vblank_disable_fn(unsigned long arg) { - struct drm_device *dev = (struct drm_device *)arg; + struct drm_vblank_crtc *vblank = (void *)arg; + struct drm_device *dev = vblank->dev; unsigned long irqflags; - int i; + int crtc = vblank->crtc; if (!dev->vblank_disable_allowed) return; - for (i = 0; i < dev->num_crtcs; i++) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - if (atomic_read(&dev->vblank[i].refcount) == 0 && - dev->vblank[i].enabled) { - DRM_DEBUG("disabling vblank on crtc %d\n", i); - vblank_disable_and_save(dev, i); - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) { + DRM_DEBUG("disabling vblank on crtc %d\n", crtc); + vblank_disable_and_save(dev, crtc); } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } +/** + * drm_vblank_cleanup - cleanup vblank support + * @dev: DRM device + * + * This function cleans up any resources allocated in drm_vblank_init. + */ void drm_vblank_cleanup(struct drm_device *dev) { + int crtc; + /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) return; - del_timer_sync(&dev->vblank_disable_timer); - - vblank_disable_fn((unsigned long)dev); + for (crtc = 0; crtc < dev->num_crtcs; crtc++) { + del_timer_sync(&dev->vblank[crtc].disable_timer); + vblank_disable_fn((unsigned long)&dev->vblank[crtc]); + } kfree(dev->vblank); @@ -174,12 +180,20 @@ void drm_vblank_cleanup(struct drm_device *dev) } EXPORT_SYMBOL(drm_vblank_cleanup); +/** + * drm_vblank_init - initialize vblank support + * @dev: drm_device + * @num_crtcs: number of crtcs supported by @dev + * + * This function initializes vblank support for @num_crtcs display pipelines. + * + * Returns: + * Zero on success or a negative error code on failure. + */ int drm_vblank_init(struct drm_device *dev, int num_crtcs) { int i, ret = -ENOMEM; - setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, - (unsigned long)dev); spin_lock_init(&dev->vbl_lock); spin_lock_init(&dev->vblank_time_lock); @@ -189,8 +203,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank) goto err; - for (i = 0; i < num_crtcs; i++) + for (i = 0; i < num_crtcs; i++) { + dev->vblank[i].dev = dev; + dev->vblank[i].crtc = i; init_waitqueue_head(&dev->vblank[i].queue); + setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn, + (unsigned long)&dev->vblank[i]); + } DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); @@ -234,13 +253,21 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) } /** - * Install IRQ handler. - * - * \param dev DRM device. + * drm_irq_install - install IRQ handler + * @dev: DRM device + * @irq: IRQ number to install the handler for * * Initializes the IRQ related data. Installs the handler, calling the driver - * \c irq_preinstall() and \c irq_postinstall() functions - * before and after the installation. + * irq_preinstall() and irq_postinstall() functions before and after the + * installation. + * + * This is the simplified helper interface provided for drivers with no special + * needs. Drivers which need to install interrupt handlers for multiple + * interrupts must instead set drm_device->irq_enabled to signal the DRM core + * that vblank interrupts are available. + * + * Returns: + * Zero on success or a negative error code on failure. */ int drm_irq_install(struct drm_device *dev, int irq) { @@ -300,11 +327,20 @@ int drm_irq_install(struct drm_device *dev, int irq) EXPORT_SYMBOL(drm_irq_install); /** - * Uninstall the IRQ handler. + * drm_irq_uninstall - uninstall the IRQ handler + * @dev: DRM device * - * \param dev DRM device. + * Calls the driver's irq_uninstall() function and unregisters the IRQ handler. + * This should only be called by drivers which used drm_irq_install() to set up + * their interrupt handler. Other drivers must only reset + * drm_device->irq_enabled to false. * - * Calls the driver's \c irq_uninstall() function, and stops the irq. + * Note that for kernel modesetting drivers it is a bug if this function fails. + * The sanity checks are only to catch buggy user modesetting drivers which call + * the same function through an ioctl. + * + * Returns: + * Zero on success or a negative error code on failure. */ int drm_irq_uninstall(struct drm_device *dev) { @@ -349,7 +385,7 @@ int drm_irq_uninstall(struct drm_device *dev) } EXPORT_SYMBOL(drm_irq_uninstall); -/** +/* * IRQ control ioctl. * * \param inode device inode. @@ -402,15 +438,14 @@ int drm_control(struct drm_device *dev, void *data, } /** - * drm_calc_timestamping_constants - Calculate vblank timestamp constants - * - * @crtc drm_crtc whose timestamp constants should be updated. - * @mode display mode containing the scanout timings + * drm_calc_timestamping_constants - calculate vblank timestamp constants + * @crtc: drm_crtc whose timestamp constants should be updated. + * @mode: display mode containing the scanout timings * * Calculate and store various constants which are later * needed by vblank and swap-completion timestamping, e.g, * by drm_calc_vbltimestamp_from_scanoutpos(). They are - * derived from crtc's true scanout timing, so they take + * derived from CRTC's true scanout timing, so they take * things like panel scaling or other adjustments into account. */ void drm_calc_timestamping_constants(struct drm_crtc *crtc, @@ -455,11 +490,22 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, EXPORT_SYMBOL(drm_calc_timestamping_constants); /** - * drm_calc_vbltimestamp_from_scanoutpos - helper routine for kms - * drivers. Implements calculation of exact vblank timestamps from - * given drm_display_mode timings and current video scanout position - * of a crtc. This can be called from within get_vblank_timestamp() - * implementation of a kms driver to implement the actual timestamping. + * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper + * @dev: DRM device + * @crtc: Which CRTC's vblank timestamp to retrieve + * @max_error: Desired maximum allowable error in timestamps (nanosecs) + * On return contains true maximum error of timestamp + * @vblank_time: Pointer to struct timeval which should receive the timestamp + * @flags: Flags to pass to driver: + * 0 = Default, + * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler + * @refcrtc: CRTC which defines scanout timing + * @mode: mode which defines the scanout timings + * + * Implements calculation of exact vblank timestamps from given drm_display_mode + * timings and current video scanout position of a CRTC. This can be called from + * within get_vblank_timestamp() implementation of a kms driver to implement the + * actual timestamping. * * Should return timestamps conforming to the OML_sync_control OpenML * extension specification. The timestamp corresponds to the end of @@ -474,21 +520,11 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * returns as no operation if a doublescan or interlaced video mode is * active. Higher level code is expected to handle this. * - * @dev: DRM device. - * @crtc: Which crtc's vblank timestamp to retrieve. - * @max_error: Desired maximum allowable error in timestamps (nanosecs). - * On return contains true maximum error of timestamp. - * @vblank_time: Pointer to struct timeval which should receive the timestamp. - * @flags: Flags to pass to driver: - * 0 = Default. - * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. - * @refcrtc: drm_crtc* of crtc which defines scanout timing. - * @mode: mode which defines the scanout timings - * - * Returns negative value on error, failure or if not supported in current + * Returns: + * Negative value on error, failure or if not supported in current * video mode: * - * -EINVAL - Invalid crtc. + * -EINVAL - Invalid CRTC. * -EAGAIN - Temporary unavailable, e.g., called before initial modeset. * -ENOTSUPP - Function not supported in current display mode. * -EIO - Failed, e.g., due to failed scanout position query. @@ -637,23 +673,23 @@ static struct timeval get_drm_timestamp(void) /** * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent - * vblank interval. - * + * vblank interval * @dev: DRM device - * @crtc: which crtc's vblank timestamp to retrieve + * @crtc: which CRTC's vblank timestamp to retrieve * @tvblank: Pointer to target struct timeval which should receive the timestamp * @flags: Flags to pass to driver: - * 0 = Default. - * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. + * 0 = Default, + * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler * * Fetches the system timestamp corresponding to the time of the most recent - * vblank interval on specified crtc. May call into kms-driver to + * vblank interval on specified CRTC. May call into kms-driver to * compute the timestamp with a high-precision GPU specific method. * * Returns zero if timestamp originates from uncorrected do_gettimeofday() * call, i.e., it isn't very precisely locked to the true vblank. * - * Returns non-zero if timestamp is considered to be very precise. + * Returns: + * Non-zero if timestamp is considered to be very precise, zero otherwise. */ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags) @@ -688,6 +724,9 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp); * Fetches the "cooked" vblank count value that represents the number of * vblank events since the system was booted, including lost events due to * modesetting activity. + * + * Returns: + * The software vblank counter. */ u32 drm_vblank_count(struct drm_device *dev, int crtc) { @@ -706,8 +745,7 @@ EXPORT_SYMBOL(drm_vblank_count); * Fetches the "cooked" vblank count value that represents the number of * vblank events since the system was booted, including lost events due to * modesetting activity. Returns corresponding system timestamp of the time - * of the vblank interval that corresponds to the current value vblank counter - * value. + * of the vblank interval that corresponds to the current vblank counter value. */ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime) @@ -836,6 +874,41 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) } /** + * drm_vblank_enable - enable the vblank interrupt on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + */ +static int drm_vblank_enable(struct drm_device *dev, int crtc) +{ + int ret = 0; + + assert_spin_locked(&dev->vbl_lock); + + spin_lock(&dev->vblank_time_lock); + + if (!dev->vblank[crtc].enabled) { + /* Enable vblank irqs under vblank_time_lock protection. + * All vblank count & timestamp updates are held off + * until we are done reinitializing master counter and + * timestamps. Filtercode in drm_handle_vblank() will + * prevent double-accounting of same vblank interval. + */ + ret = dev->driver->enable_vblank(dev, crtc); + DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret); + if (ret) + atomic_dec(&dev->vblank[crtc].refcount); + else { + dev->vblank[crtc].enabled = true; + drm_update_vblank_count(dev, crtc); + } + } + + spin_unlock(&dev->vblank_time_lock); + + return ret; +} + +/** * drm_vblank_get - get a reference count on vblank events * @dev: DRM device * @crtc: which CRTC to own @@ -843,36 +916,20 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) * Acquire a reference count on vblank events to avoid having them disabled * while in use. * - * RETURNS + * This is the legacy version of drm_crtc_vblank_get(). + * + * Returns: * Zero on success, nonzero on failure. */ int drm_vblank_get(struct drm_device *dev, int crtc) { - unsigned long irqflags, irqflags2; + unsigned long irqflags; int ret = 0; spin_lock_irqsave(&dev->vbl_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) { - spin_lock_irqsave(&dev->vblank_time_lock, irqflags2); - if (!dev->vblank[crtc].enabled) { - /* Enable vblank irqs under vblank_time_lock protection. - * All vblank count & timestamp updates are held off - * until we are done reinitializing master counter and - * timestamps. Filtercode in drm_handle_vblank() will - * prevent double-accounting of same vblank interval. - */ - ret = dev->driver->enable_vblank(dev, crtc); - DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", - crtc, ret); - if (ret) - atomic_dec(&dev->vblank[crtc].refcount); - else { - dev->vblank[crtc].enabled = true; - drm_update_vblank_count(dev, crtc); - } - } - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2); + ret = drm_vblank_enable(dev, crtc); } else { if (!dev->vblank[crtc].enabled) { atomic_dec(&dev->vblank[crtc].refcount); @@ -886,12 +943,32 @@ int drm_vblank_get(struct drm_device *dev, int crtc) EXPORT_SYMBOL(drm_vblank_get); /** + * drm_crtc_vblank_get - get a reference count on vblank events + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * This is the native kms version of drm_vblank_off(). + * + * Returns: + * Zero on success, nonzero on failure. + */ +int drm_crtc_vblank_get(struct drm_crtc *crtc) +{ + return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_get); + +/** * drm_vblank_put - give up ownership of vblank events * @dev: DRM device * @crtc: which counter to give up * * Release ownership of a given vblank counter, turning off interrupts * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the legacy version of drm_crtc_vblank_put(). */ void drm_vblank_put(struct drm_device *dev, int crtc) { @@ -900,17 +977,39 @@ void drm_vblank_put(struct drm_device *dev, int crtc) /* Last user schedules interrupt disable */ if (atomic_dec_and_test(&dev->vblank[crtc].refcount) && (drm_vblank_offdelay > 0)) - mod_timer(&dev->vblank_disable_timer, + mod_timer(&dev->vblank[crtc].disable_timer, jiffies + ((drm_vblank_offdelay * HZ)/1000)); } EXPORT_SYMBOL(drm_vblank_put); /** + * drm_crtc_vblank_put - give up ownership of vblank events + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the native kms version of drm_vblank_put(). + */ +void drm_crtc_vblank_put(struct drm_crtc *crtc) +{ + drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_put); + +/** * drm_vblank_off - disable vblank events on a CRTC * @dev: DRM device * @crtc: CRTC in question * - * Caller must hold event lock. + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on() can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + * + * This is the legacy version of drm_crtc_vblank_off(). */ void drm_vblank_off(struct drm_device *dev, int crtc) { @@ -944,12 +1043,87 @@ void drm_vblank_off(struct drm_device *dev, int crtc) EXPORT_SYMBOL(drm_vblank_off); /** + * drm_crtc_vblank_off - disable vblank events on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + * + * This is the native kms version of drm_vblank_off(). + */ +void drm_crtc_vblank_off(struct drm_crtc *crtc) +{ + drm_vblank_off(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_off); + +/** + * drm_vblank_on - enable vblank events on a CRTC + * @dev: DRM device + * @crtc: CRTC in question + * + * This functions restores the vblank interrupt state captured with + * drm_vblank_off() again. Note that calls to drm_vblank_on() and + * drm_vblank_off() can be unbalanced and so can also be unconditionaly called + * in driver load code to reflect the current hardware state of the crtc. + * + * This is the legacy version of drm_crtc_vblank_on(). + */ +void drm_vblank_on(struct drm_device *dev, int crtc) +{ + unsigned long irqflags; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* re-enable interrupts if there's are users left */ + if (atomic_read(&dev->vblank[crtc].refcount) != 0) + WARN_ON(drm_vblank_enable(dev, crtc)); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +} +EXPORT_SYMBOL(drm_vblank_on); + +/** + * drm_crtc_vblank_on - enable vblank events on a CRTC + * @crtc: CRTC in question + * + * This functions restores the vblank interrupt state captured with + * drm_vblank_off() again. Note that calls to drm_vblank_on() and + * drm_vblank_off() can be unbalanced and so can also be unconditionaly called + * in driver load code to reflect the current hardware state of the crtc. + * + * This is the native kms version of drm_vblank_on(). + */ +void drm_crtc_vblank_on(struct drm_crtc *crtc) +{ + drm_vblank_on(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_on); + +/** * drm_vblank_pre_modeset - account for vblanks across mode sets * @dev: DRM device * @crtc: CRTC in question * * Account for vblank events across mode setting events, which will likely * reset the hardware frame counter. + * + * This is done by grabbing a temporary vblank reference to ensure that the + * vblank interrupt keeps running across the modeset sequence. With this the + * software-side vblank frame counting will ensure that there are no jumps or + * discontinuities. + * + * Unfortunately this approach is racy and also doesn't work when the vblank + * interrupt stops running, e.g. across system suspend resume. It is therefore + * highly recommended that drivers use the newer drm_vblank_off() and + * drm_vblank_on() instead. drm_vblank_pre_modeset() only works correctly when + * using "cooked" software vblank frame counters and not relying on any hardware + * counters. + * + * Drivers must call drm_vblank_post_modeset() when re-enabling the same crtc + * again. */ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) { @@ -971,6 +1145,14 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_pre_modeset); +/** + * drm_vblank_post_modeset - undo drm_vblank_pre_modeset changes + * @dev: DRM device + * @crtc: CRTC in question + * + * This function again drops the temporary vblank reference acquired in + * drm_vblank_pre_modeset. + */ void drm_vblank_post_modeset(struct drm_device *dev, int crtc) { unsigned long irqflags; @@ -992,7 +1174,7 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc) } EXPORT_SYMBOL(drm_vblank_post_modeset); -/** +/* * drm_modeset_ctl - handle vblank event counter changes across mode switch * @DRM_IOCTL_ARGS: standard ioctl arguments * @@ -1105,7 +1287,7 @@ err_put: return ret; } -/** +/* * Wait for VBLANK. * * \param inode device inode. @@ -1116,7 +1298,7 @@ err_put: * * This function enables the vblank interrupt on the pipe requested, then * sleeps waiting for the requested sequence number to occur, and drops - * the vblank interrupt refcount afterwards. (vblank irq disable follows that + * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that * after a timeout with no further vblank waits scheduled). */ int drm_wait_vblank(struct drm_device *dev, void *data, @@ -1187,6 +1369,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ, (((drm_vblank_count(dev, crtc) - vblwait->request.sequence) <= (1 << 23)) || + !dev->vblank[crtc].enabled || !dev->irq_enabled)); if (ret != -EINTR) { diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index d2b1c03b3d7..d966afa7eca 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -124,7 +124,6 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, .y2 = crtc->mode.vdisplay, }; struct drm_connector **connector_list; - struct drm_framebuffer *tmpfb; int num_connectors, ret; if (!crtc->enabled) { @@ -145,6 +144,8 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, } /* Disallow scaling */ + src_w >>= 16; + src_h >>= 16; if (crtc_w != src_w || crtc_h != src_h) { DRM_DEBUG_KMS("Can't scale primary plane\n"); return -EINVAL; @@ -176,21 +177,14 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, set.num_connectors = num_connectors; /* - * set_config() adjusts crtc->primary->fb; however the DRM setplane - * code that called us expects to handle the framebuffer update and - * reference counting; save and restore the current fb before - * calling it. - * - * N.B., we call set_config() directly here rather than using + * We call set_config() directly here rather than using * drm_mode_set_config_internal. We're reprogramming the same * connectors that were already in use, so we shouldn't need the extra * cross-CRTC fb refcounting to accomodate stealing connectors. * drm_mode_setplane() already handles the basic refcounting for the * framebuffers involved in this operation. */ - tmpfb = plane->fb; ret = crtc->funcs->set_config(&set); - plane->fb = tmpfb; kfree(connector_list); return ret; @@ -232,7 +226,6 @@ EXPORT_SYMBOL(drm_primary_helper_disable); */ void drm_primary_helper_destroy(struct drm_plane *plane) { - plane->funcs->disable_plane(plane); drm_plane_cleanup(plane); kfree(plane); } diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 8afdd0998a8..79f07f2c13d 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -151,7 +151,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect drm_mode_validate_flag(connector, mode_flags); list_for_each_entry(mode, &connector->modes, head) { - if (mode->status == MODE_OK) + if (mode->status == MODE_OK && connector_funcs->mode_valid) mode->status = connector_funcs->mode_valid(connector, mode); } diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 1447b0ee367..3727ac8bc31 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -294,6 +294,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type) slot = drm_minor_get_slot(dev, type); if (*slot) { + drm_mode_group_destroy(&(*slot)->mode_group); kfree(*slot); *slot = NULL; } diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index aed533bbfd3..bb74472b4e4 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -949,12 +949,6 @@ static int exynos_dp_get_modes(struct drm_connector *connector) return 1; } -static int exynos_dp_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder *exynos_dp_best_encoder( struct drm_connector *connector) { @@ -965,7 +959,6 @@ static struct drm_encoder *exynos_dp_best_encoder( static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .get_modes = exynos_dp_get_modes, - .mode_valid = exynos_dp_mode_valid, .best_encoder = exynos_dp_best_encoder, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 2b09c7c0bfc..82e52c71bcc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -94,12 +94,6 @@ static int exynos_dpi_get_modes(struct drm_connector *connector) return 0; } -static int exynos_dpi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder * exynos_dpi_best_encoder(struct drm_connector *connector) { @@ -110,7 +104,6 @@ exynos_dpi_best_encoder(struct drm_connector *connector) static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = { .get_modes = exynos_dpi_get_modes, - .mode_valid = exynos_dpi_mode_valid, .best_encoder = exynos_dpi_best_encoder, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 852f2dadaeb..3fa987df906 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -533,12 +533,6 @@ static int vidi_get_modes(struct drm_connector *connector) return drm_add_edid_modes(connector, edid); } -static int vidi_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) { struct vidi_context *ctx = ctx_from_connector(connector); @@ -548,7 +542,6 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { .get_modes = vidi_get_modes, - .mode_valid = vidi_mode_valid, .best_encoder = vidi_best_encoder, }; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 787ad93c5fa..0aaa13ab770 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -455,8 +455,8 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, * * Returns the previous state of underrun reporting. */ -bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, - enum pipe pipe, bool enable) +static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7def8261eaa..5f803f43687 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3910,7 +3910,7 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc) int plane = intel_crtc->plane; intel_crtc_wait_for_pending_flips(crtc); - drm_vblank_off(dev, pipe); + drm_crtc_vblank_off(crtc); if (dev_priv->fbc.plane == plane) intel_disable_fbc(dev); @@ -4000,6 +4000,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc_enable_planes(crtc); + drm_crtc_vblank_on(crtc); } /* IPS only exists on ULT machines and is tied to pipe A. */ @@ -4113,6 +4114,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) * to change the workaround. */ haswell_mode_set_planes_workaround(intel_crtc); intel_crtc_enable_planes(crtc); + + drm_crtc_vblank_on(crtc); } static void ironlake_pfit_disable(struct intel_crtc *crtc) @@ -4620,6 +4623,8 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) encoder->enable(encoder); intel_crtc_enable_planes(crtc); + + drm_crtc_vblank_on(crtc); } static void i9xx_set_pll_dividers(struct intel_crtc *crtc) @@ -4697,6 +4702,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) encoder->enable(encoder); intel_crtc_enable_planes(crtc); + + drm_crtc_vblank_on(crtc); } static void i9xx_pfit_disable(struct intel_crtc *crtc) @@ -8815,7 +8822,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, if (work->event) drm_send_vblank_event(dev, intel_crtc->pipe, work->event); - drm_vblank_put(dev, intel_crtc->pipe); + drm_crtc_vblank_put(crtc); spin_unlock_irqrestore(&dev->event_lock, flags); @@ -9197,7 +9204,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->old_fb_obj = to_intel_framebuffer(old_fb)->obj; INIT_WORK(&work->work, intel_unpin_work_fn); - ret = drm_vblank_get(dev, intel_crtc->pipe); + ret = drm_crtc_vblank_get(crtc); if (ret) goto free_work; @@ -9206,7 +9213,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (intel_crtc->unpin_work) { spin_unlock_irqrestore(&dev->event_lock, flags); kfree(work); - drm_vblank_put(dev, intel_crtc->pipe); + drm_crtc_vblank_put(crtc); DRM_DEBUG_DRIVER("flip queue: crtc already busy\n"); return -EBUSY; @@ -9280,7 +9287,7 @@ cleanup: intel_crtc->unpin_work = NULL; spin_unlock_irqrestore(&dev->event_lock, flags); - drm_vblank_put(dev, intel_crtc->pipe); + drm_crtc_vblank_put(crtc); free_work: kfree(work); @@ -10901,6 +10908,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + + WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe); } enum pipe intel_get_pipe_from_connector(struct intel_connector *connector) @@ -11838,11 +11847,18 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) encoder->base.crtc = NULL; } } - if (crtc->active) { + + if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) { /* * We start out with underrun reporting disabled to avoid races. * For correct bookkeeping mark this on active crtcs. * + * Also on gmch platforms we dont have any hardware bits to + * disable the underrun reporting. Which means we need to start + * out with underrun reporting disabled also on inactive pipes, + * since otherwise we'll complain about the garbage we read when + * e.g. coming up after runtime pm. + * * No protection against concurrent access is required - at * worst a fifo underrun happens which also sets this to false. */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index df7808cbb11..a78eb75411c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -668,8 +668,6 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) /* i915_irq.c */ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, enum pipe pipe, bool enable); -bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, - enum pipe pipe, bool enable); bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, enum transcoder pch_transcoder, bool enable); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 4eff29f8621..b491a899d1b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5635,33 +5635,6 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) } } -static void reset_vblank_counter(struct drm_device *dev, enum pipe pipe) -{ - assert_spin_locked(&dev->vbl_lock); - - dev->vblank[pipe].last = 0; -} - -static void hsw_power_well_post_disable(struct drm_i915_private *dev_priv) -{ - struct drm_device *dev = dev_priv->dev; - enum pipe pipe; - unsigned long irqflags; - - /* - * After this, the registers on the pipes that are part of the power - * well will become zero, so we have to adjust our counters according to - * that. - * - * FIXME: Should we do this in general in drm_vblank_post_modeset? - */ - spin_lock_irqsave(&dev->vbl_lock, irqflags); - for_each_pipe(pipe) - if (pipe != PIPE_A) - reset_vblank_counter(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -} - static void hsw_set_power_well(struct drm_i915_private *dev_priv, struct i915_power_well *power_well, bool enable) { @@ -5690,8 +5663,6 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv, I915_WRITE(HSW_PWR_WELL_DRIVER, 0); POSTING_READ(HSW_PWR_WELL_DRIVER); DRM_DEBUG_KMS("Requesting to disable the power well\n"); - - hsw_power_well_post_disable(dev_priv); } } } @@ -5848,23 +5819,12 @@ static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv, static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { - struct drm_device *dev = dev_priv->dev; - enum pipe pipe; - WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DISP2D); spin_lock_irq(&dev_priv->irq_lock); - for_each_pipe(pipe) - __intel_set_cpu_fifo_underrun_reporting(dev, pipe, false); - valleyview_disable_display_irqs(dev_priv); spin_unlock_irq(&dev_priv->irq_lock); - spin_lock_irq(&dev->vbl_lock); - for_each_pipe(pipe) - reset_vblank_counter(dev, pipe); - spin_unlock_irq(&dev->vbl_lock); - vlv_set_power_well(dev_priv, power_well, false); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 4f3ba93cd91..289048d1c7b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -57,15 +57,8 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) return 1; } -static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_lvds_connector_get_modes, - .mode_valid = rcar_du_lvds_connector_mode_valid, .best_encoder = rcar_du_connector_best_encoder, }; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 41d563adfea..ccfe64c7188 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -25,15 +25,8 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) return 0; } -static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = rcar_du_vga_connector_get_modes, - .mode_valid = rcar_du_vga_connector_mode_valid, .best_encoder = rcar_du_connector_best_encoder, }; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index e9e5e6d368c..faf176b2daf 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -674,12 +674,6 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector) return 1; } -static int shmob_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} - static struct drm_encoder * shmob_drm_connector_best_encoder(struct drm_connector *connector) { @@ -690,7 +684,6 @@ shmob_drm_connector_best_encoder(struct drm_connector *connector) static const struct drm_connector_helper_funcs connector_helper_funcs = { .get_modes = shmob_drm_connector_get_modes, - .mode_valid = shmob_drm_connector_mode_valid, .best_encoder = shmob_drm_connector_best_encoder, }; diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 4144a75e5f7..53ff005245c 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -200,13 +200,6 @@ static const struct file_operations imx_drm_driver_fops = { .llseek = noop_llseek, }; -int imx_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - return MODE_OK; -} -EXPORT_SYMBOL(imx_drm_connector_mode_valid); - void imx_drm_connector_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h index a322bac5541..7453ae00c41 100644 --- a/drivers/staging/imx-drm/imx-drm.h +++ b/drivers/staging/imx-drm/imx-drm.h @@ -50,8 +50,6 @@ int imx_drm_encoder_get_mux_id(struct device_node *node, int imx_drm_encoder_parse_of(struct drm_device *drm, struct drm_encoder *encoder, struct device_node *np); -int imx_drm_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode); void imx_drm_connector_destroy(struct drm_connector *connector); void imx_drm_encoder_destroy(struct drm_encoder *encoder); diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c index d47dedd2cdb..9fbe6d6a989 100644 --- a/drivers/staging/imx-drm/imx-hdmi.c +++ b/drivers/staging/imx-drm/imx-hdmi.c @@ -1492,7 +1492,6 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = { static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { .get_modes = imx_hdmi_connector_get_modes, - .mode_valid = imx_drm_connector_mode_valid, .best_encoder = imx_hdmi_connector_best_encoder, }; diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c index fe4c1ef4e7a..7e3f019d7e7 100644 --- a/drivers/staging/imx-drm/imx-ldb.c +++ b/drivers/staging/imx-drm/imx-ldb.c @@ -317,7 +317,6 @@ static struct drm_connector_funcs imx_ldb_connector_funcs = { static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { .get_modes = imx_ldb_connector_get_modes, .best_encoder = imx_ldb_connector_best_encoder, - .mode_valid = imx_drm_connector_mode_valid, }; static struct drm_encoder_funcs imx_ldb_encoder_funcs = { diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index 575533f4fd6..5a5a5287a86 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -251,10 +251,6 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, unsigned long rate; int ret; - ret = imx_drm_connector_mode_valid(connector, mode); - if (ret != MODE_OK) - return ret; - /* pixel clock with 2x oversampling */ rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; if (rate == mode->clock) diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c index c60b6c645f4..52598e489a4 100644 --- a/drivers/staging/imx-drm/parallel-display.c +++ b/drivers/staging/imx-drm/parallel-display.c @@ -148,7 +148,6 @@ static struct drm_connector_funcs imx_pd_connector_funcs = { static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { .get_modes = imx_pd_connector_get_modes, .best_encoder = imx_pd_connector_best_encoder, - .mode_valid = imx_drm_connector_mode_valid, }; static struct drm_encoder_funcs imx_pd_encoder_funcs = { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 12f10bc2395..76ccaabd041 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1024,14 +1024,17 @@ struct drm_pending_vblank_event { }; struct drm_vblank_crtc { + struct drm_device *dev; /* pointer to the drm_device */ wait_queue_head_t queue; /**< VBLANK wait queue */ struct timeval time[DRM_VBLANKTIME_RBSIZE]; /**< timestamp of current count */ + struct timer_list disable_timer; /* delayed disable timer */ atomic_t count; /**< number of VBLANK interrupts */ atomic_t refcount; /* number of users of vblank interruptsper crtc */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ u32 last_wait; /* Last vblank seqno waited per CRTC */ unsigned int inmodeset; /* Display driver is setting mode */ + int crtc; /* crtc index */ bool enabled; /* so we don't call enable more than once per disable */ }; @@ -1119,7 +1122,6 @@ struct drm_device { spinlock_t vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */ spinlock_t vbl_lock; - struct timer_list vblank_disable_timer; u32 max_vblank_count; /**< size of vblank counter register */ @@ -1357,8 +1359,14 @@ extern void drm_send_vblank_event(struct drm_device *dev, int crtc, extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); +extern int drm_crtc_vblank_get(struct drm_crtc *crtc); +extern void drm_crtc_vblank_put(struct drm_crtc *crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); +extern void drm_vblank_on(struct drm_device *dev, int crtc); +extern void drm_crtc_vblank_off(struct drm_crtc *crtc); +extern void drm_crtc_vblank_on(struct drm_crtc *crtc); extern void drm_vblank_cleanup(struct drm_device *dev); + extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags); extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e55fccbe7c4..698d54e27f3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -915,6 +915,7 @@ extern const char *drm_get_tv_subconnector_name(int val); extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); +extern void drm_mode_group_destroy(struct drm_mode_group *group); extern bool drm_probe_ddc(struct i2c_adapter *adapter); extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); @@ -1020,6 +1021,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern u8 drm_match_cea_mode(const struct drm_display_mode *to_match); +extern enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code); extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid); extern bool drm_rgb_quant_range_selectable(struct edid *edid); diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index f51c8393e9a..a3d75fefd01 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -114,7 +114,7 @@ struct drm_encoder_helper_funcs { /** * drm_connector_helper_funcs - helper operations for connectors * @get_modes: get mode list for this connector - * @mode_valid: is this mode valid on the given connector? + * @mode_valid (optional): is this mode valid on the given connector? * * The helper operations are called by the mid-layer CRTC helper. */ diff --git a/include/drm/drm_flip_work.h b/include/drm/drm_flip_work.h index 35c776ae7d3..9eed34dcd6a 100644 --- a/include/drm/drm_flip_work.h +++ b/include/drm/drm_flip_work.h @@ -57,6 +57,7 @@ typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val); * @count: number of committed items * @func: callback fxn called for each committed item * @worker: worker which calls @func + * @fifo: queue of committed items */ struct drm_flip_work { const char *name; |