From 8cca1f0487a6ccadf969dc7f3a9baae2f5cbafa8 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann <tdz@users.sourceforge.net> Date: Fri, 26 Feb 2016 18:01:17 +0100 Subject: [PATCH] drm/sisvga: Add KMS driver for SiS 6326 Signed-off-by: Thomas Zimmermann <tdz@users.sourceforge.net> --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/sisvga/Kconfig | 13 + drivers/gpu/drm/sisvga/Makefile | 17 + drivers/gpu/drm/sisvga/sisvga_bo.c | 225 +++++ drivers/gpu/drm/sisvga/sisvga_connector.c | 244 +++++ drivers/gpu/drm/sisvga/sisvga_crtc.c | 979 ++++++++++++++++++++ drivers/gpu/drm/sisvga/sisvga_ddc.c | 411 ++++++++ drivers/gpu/drm/sisvga/sisvga_debug.c | 303 ++++++ drivers/gpu/drm/sisvga/sisvga_debug.h | 17 + drivers/gpu/drm/sisvga/sisvga_device.c | 592 ++++++++++++ drivers/gpu/drm/sisvga/sisvga_device.h | 268 ++++++ drivers/gpu/drm/sisvga/sisvga_drv.c | 505 ++++++++++ drivers/gpu/drm/sisvga/sisvga_drv.h | 24 + drivers/gpu/drm/sisvga/sisvga_encoder.c | 213 +++++ drivers/gpu/drm/sisvga/sisvga_fbdev.c | 288 ++++++ drivers/gpu/drm/sisvga/sisvga_framebuffer.c | 93 ++ drivers/gpu/drm/sisvga/sisvga_modes.c | 42 + drivers/gpu/drm/sisvga/sisvga_modes.h | 46 + drivers/gpu/drm/sisvga/sisvga_plane.c | 127 +++ drivers/gpu/drm/sisvga/sisvga_ttm.c | 275 ++++++ drivers/gpu/drm/sisvga/sisvga_vclk.c | 167 ++++ drivers/gpu/drm/sisvga/sisvga_vclk.h | 57 ++ include/linux/pci_ids.h | 2 + 24 files changed, 4911 insertions(+) create mode 100644 drivers/gpu/drm/sisvga/Kconfig create mode 100644 drivers/gpu/drm/sisvga/Makefile create mode 100644 drivers/gpu/drm/sisvga/sisvga_bo.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_connector.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_crtc.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_ddc.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_debug.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_debug.h create mode 100644 drivers/gpu/drm/sisvga/sisvga_device.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_device.h create mode 100644 drivers/gpu/drm/sisvga/sisvga_drv.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_drv.h create mode 100644 drivers/gpu/drm/sisvga/sisvga_encoder.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_fbdev.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_framebuffer.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_modes.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_modes.h create mode 100644 drivers/gpu/drm/sisvga/sisvga_plane.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_ttm.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_vclk.c create mode 100644 drivers/gpu/drm/sisvga/sisvga_vclk.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 10f9f01123ead..2c73d5f7abca8 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -304,6 +304,8 @@ source "drivers/gpu/drm/tve200/Kconfig" source "drivers/gpu/drm/xen/Kconfig" +source "drivers/gpu/drm/sisvga/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8873d47691163..414163bdbea3f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_DRM_V3D) += v3d/ obj-$(CONFIG_DRM_VC4) += vc4/ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ obj-$(CONFIG_DRM_SIS) += sis/ +obj-$(CONFIG_DRM_SISVGA)+= sisvga/ obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ diff --git a/drivers/gpu/drm/sisvga/Kconfig b/drivers/gpu/drm/sisvga/Kconfig new file mode 100644 index 0000000000000..ce039f1d91be4 --- /dev/null +++ b/drivers/gpu/drm/sisvga/Kconfig @@ -0,0 +1,13 @@ +config DRM_SISVGA + tristate "SiS video cards (KMS driver)" + depends on DRM && PCI && AGP + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + help + Choose this option if you have a SiS 305 or compatible video + chipset. If M is selected the module will be called sisvga. AGP + support is required for this driver to work. diff --git a/drivers/gpu/drm/sisvga/Makefile b/drivers/gpu/drm/sisvga/Makefile new file mode 100644 index 0000000000000..2e9ec45489190 --- /dev/null +++ b/drivers/gpu/drm/sisvga/Makefile @@ -0,0 +1,17 @@ +ccflags-y = -Iinclude/drm +sisvga-y := sisvga_bo.o \ + sisvga_connector.o \ + sisvga_crtc.o \ + sisvga_ddc.o \ + sisvga_debug.o \ + sisvga_device.o \ + sisvga_drv.o \ + sisvga_encoder.o \ + sisvga_fbdev.o \ + sisvga_framebuffer.o \ + sisvga_modes.o \ + sisvga_plane.o \ + sisvga_ttm.o \ + sisvga_vclk.o + +obj-$(CONFIG_DRM_SISVGA) += sisvga.o diff --git a/drivers/gpu/drm/sisvga/sisvga_bo.c b/drivers/gpu/drm/sisvga/sisvga_bo.c new file mode 100644 index 0000000000000..29a50a144e283 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_bo.c @@ -0,0 +1,225 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" + +/* + * Buffer objects + */ + +void sisvga_bo_ttm_placement(struct sisvga_bo *bo, int domain) +{ + u32 c = 0; + unsigned i; + + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + + if (domain & TTM_PL_FLAG_VRAM) + bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + + if (domain & TTM_PL_FLAG_SYSTEM) + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + if (!c) + bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; + + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } +} + +static void sisvga_bo_ttm_destroy(struct ttm_buffer_object *tbo) +{ + struct sisvga_bo *sis_bo; + + sis_bo = container_of(tbo, struct sisvga_bo, bo); + + drm_gem_object_release(&sis_bo->gem); + kfree(sis_bo); +} + +int sisvga_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct sisvga_bo **sis_bo_out) +{ + struct sisvga_device *sis_dev = dev->dev_private; + struct sisvga_bo *sis_bo; + size_t acc_size; + int ret; + + sis_bo = kzalloc(sizeof(struct sisvga_bo), GFP_KERNEL); + if (!sis_bo) + return -ENOMEM; + + ret = drm_gem_object_init(dev, &sis_bo->gem, size); + if (ret) { + kfree(sis_bo); + return ret; + } + + sis_bo->bo.bdev = &sis_dev->ttm.bdev; + + sisvga_bo_ttm_placement(sis_bo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&sis_dev->ttm.bdev, size, + sizeof(struct sisvga_bo)); + + ret = ttm_bo_init(&sis_dev->ttm.bdev, &sis_bo->bo, size, + ttm_bo_type_device, &sis_bo->placement, + align >> PAGE_SHIFT, false, acc_size, + NULL, NULL, sisvga_bo_ttm_destroy); + if (ret) + return ret; + + *sis_bo_out = sis_bo; + return 0; +} + +void sisvga_bo_unref(struct sisvga_bo **sis_bo) +{ + struct ttm_buffer_object *tbo; + + if ((*sis_bo) == NULL) + return; + + tbo = &((*sis_bo)->bo); + ttm_bo_unref(&tbo); + *sis_bo = NULL; +} + +int sisvga_bo_reserve(struct sisvga_bo *bo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL); + if (ret) { + if (ret != -ERESTARTSYS && ret != -EBUSY) + DRM_ERROR("sisvga: ttm_bo_reserve(%p) failed," + " error %d\n", bo, -ret); + return ret; + } + return 0; +} + +void sisvga_bo_unreserve(struct sisvga_bo *bo) +{ + ttm_bo_unreserve(&bo->bo); +} + +u64 sisvga_bo_mmap_offset(struct sisvga_bo *sis_bo) +{ + return drm_vma_node_offset_addr(&sis_bo->bo.vma_node); +} + +int sisvga_bo_pin(struct sisvga_bo *bo, u32 pl_flag, u64 *gpu_addr) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (bo->pin_count) { + bo->pin_count++; + goto out; + } + + sisvga_bo_ttm_placement(bo, pl_flag); + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx); + if (ret) { + DRM_ERROR("sisvga: ttm_bo_validate failed, error %d\n", -ret); + return ret; + } + + bo->pin_count = 1; + +out: + if (gpu_addr) + *gpu_addr = bo->bo.offset; + + return 0; +} + +int sisvga_bo_unpin(struct sisvga_bo *bo) +{ + int i; + struct ttm_operation_ctx ctx = { false, false }; + + if (!bo->pin_count) { + DRM_ERROR("sisvga: BO %p is not pinned \n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + + return ttm_bo_validate(&bo->bo, &bo->placement, &ctx); +} + +int sisvga_bo_push_to_system(struct sisvga_bo *bo) +{ + int i, ret; + struct ttm_operation_ctx ctx = { false, false }; + + if (!bo->pin_count) { + DRM_ERROR("sisvga: BO %p is not pinned \n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + + sisvga_bo_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); + for (i = 0; i < bo->placement.num_placement ; i++) + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx); + if (ret) { + DRM_ERROR("sisvga: ttm_bo_validate failed, error %d\n", -ret); + return ret; + } + + return 0; +} + +/* + * GEM objects + */ + +int sisvga_gem_create(struct drm_device *dev, u32 size, bool iskernel, + struct drm_gem_object **obj) +{ + struct sisvga_bo *sis_bo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = sisvga_bo_create(dev, size, 0, 0, &sis_bo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("sisvga: sisvga_bo_create() failed," + " error %d\n", -ret); + return ret; + } + *obj = &sis_bo->gem; + return 0; +} diff --git a/drivers/gpu/drm/sisvga/sisvga_connector.c b/drivers/gpu/drm/sisvga/sisvga_connector.c new file mode 100644 index 0000000000000..18ca4d6d680f9 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_connector.c @@ -0,0 +1,244 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_modeset_helper_vtables.h> +#include "sisvga_vclk.h" + +static struct sisvga_connector* sisvga_connector_of( + struct drm_connector* connector) +{ + return container_of(connector, struct sisvga_connector, base); +} + +/* + * Connector helper funcs + */ + +static int sisvga_connector_helper_get_modes_vga( + struct drm_connector *connector) +{ + struct sisvga_connector *sis_connector; + struct edid *edid; + int ret; + + sis_connector = sisvga_connector_of(connector); + + edid = drm_get_edid(connector, &sis_connector->ddc.adapter); + if (!edid) + return 0; + + ret = drm_mode_connector_update_edid_property(connector, edid); + if (ret < 0) + goto err; + + ret = drm_add_edid_modes(connector, edid); + if (ret < 0) + goto err; + + kfree(edid); + + return 0; + +err: + kfree(edid); + return ret; +} + +static int sisvga_connector_helper_mode_valid_vga( + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + enum sisvga_vclk vclk; + int ret; + struct sisvga_device *sdev = connector->dev->dev_private; + const struct sisvga_device_info *info = sdev->info; + const struct sisvga_mode* smode; + int bpp = info->max_bpp; + + /* validate dotclock */ + + if (mode->clock > info->max_clock) + return MODE_CLOCK_HIGH; + ret = sisvga_vclk_of_clock(mode->clock, &vclk); + if (ret < 0) + return MODE_CLOCK_RANGE; + if (!(info->supported_vclks & SISVGA_VCLK_BIT(vclk))) + return MODE_CLOCK_RANGE; + + /* validate display size */ + + if (mode->hdisplay > info->max_hdisplay) + return MODE_VIRTUAL_X; + if (mode->vdisplay > info->max_vdisplay) + return MODE_VIRTUAL_Y; + + if (((mode->hdisplay % 8) != 0 || + (mode->hsync_start % 8) != 0 || + (mode->hsync_end % 8) != 0 || + (mode->htotal % 8) != 0) && + ((mode->hdisplay % 9) != 0 || + (mode->hsync_start % 9) != 0 || + (mode->hsync_end % 9) != 0 || + (mode->htotal % 9) != 0)) { + return MODE_H_ILLEGAL; + } + + if (mode->hdisplay > info->max_hdisplay || + mode->hsync_start > info->max_hsync_start || + mode->hsync_end > info->max_hsync_end || + mode->htotal > info->max_htotal || + mode->vdisplay > info->max_vdisplay || + mode->vsync_start > info->max_vsync_start || + mode->vsync_end > info->max_vsync_end || + mode->vtotal > info->max_vtotal) { + return MODE_BAD; + } + + /* validate memory requirements */ + + if (connector->cmdline_mode.specified) { + if (connector->cmdline_mode.bpp_specified) + bpp = connector->cmdline_mode.bpp; + } + + if ((mode->hdisplay * mode->vdisplay * (bpp / 8)) > sdev->vram.size) { + if (connector->cmdline_mode.specified) + connector->cmdline_mode.specified = false; + return MODE_BAD; + } + + /* see if the mode is supported by the device */ + + smode = sisvga_find_compatible_mode(info->vga_modes, + info->vga_modes + + info->vga_modes_len, + mode, bpp); + if (!smode) + return MODE_BAD; + + return MODE_OK; +} + +static struct drm_encoder* sisvga_connector_helper_best_encoder_vga( + struct drm_connector *connector) +{ + struct drm_encoder *encoder; + size_t i; + size_t len = ARRAY_SIZE(connector->encoder_ids); + + for (i = 0; (i < len) && connector->encoder_ids[i]; ++i) { + + encoder = drm_encoder_find(connector->dev, NULL, + connector->encoder_ids[i]); + if (!encoder) { + DRM_ERROR("encoder %d not found", + connector->encoder_ids[i]); + continue; + } + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) + continue; + + return encoder; + } + + return NULL; +} + +static const struct drm_connector_helper_funcs sisvga_connector_helper_funcs = { + .get_modes = sisvga_connector_helper_get_modes_vga, + .mode_valid = sisvga_connector_helper_mode_valid_vga, + .best_encoder = sisvga_connector_helper_best_encoder_vga, +}; + +/* + * Connector funcs + */ + +static enum drm_connector_status sisvga_connector_detect_vga( + struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void sisvga_connector_destroy(struct drm_connector *connector) +{ + struct sisvga_connector *sis_connector = sisvga_connector_of(connector); + struct drm_device *dev = connector->dev; + + sisvga_ddc_fini(&sis_connector->ddc); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + devm_kfree(dev->dev, sis_connector); +} + +static const struct drm_connector_funcs sisvga_connector_funcs_vga = { + .dpms = drm_helper_connector_dpms, + .detect = sisvga_connector_detect_vga, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = sisvga_connector_destroy, +}; + +/* + * struct sisvga_connector + */ + +static int sisvga_connector_init_vga(struct sisvga_connector *sis_connector, + struct drm_device *dev) +{ + int ret; + struct drm_connector *connector = &sis_connector->base; + + ret = drm_connector_init(dev, connector, &sisvga_connector_funcs_vga, + DRM_MODE_CONNECTOR_VGA); + if (ret < 0) + return ret; + + drm_connector_helper_add(connector, &sisvga_connector_helper_funcs); + + ret = sisvga_ddc_init(&sis_connector->ddc, dev); + if (ret < 0) + goto err_sisvga_ddc_init; + + ret = drm_connector_register(connector); + if (ret < 0) + goto err_drm_connector_register; + + return 0; + +err_drm_connector_register: + sisvga_ddc_fini(&sis_connector->ddc); +err_sisvga_ddc_init: + drm_connector_cleanup(connector); + return ret; +} + +struct sisvga_connector* sisvga_connector_create_vga(struct drm_device *dev) +{ + struct sisvga_connector *sis_connector; + int ret; + + sis_connector = devm_kzalloc(dev->dev, sizeof(*sis_connector), + GFP_KERNEL); + if (!sis_connector) + return ERR_PTR(-ENOMEM); + + ret = sisvga_connector_init_vga(sis_connector, dev); + if (ret < 0) + goto err_sisvga_connector_init_vga; + + return sis_connector; + +err_sisvga_connector_init_vga: + devm_kfree(dev->dev, sis_connector); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_crtc.c b/drivers/gpu/drm/sisvga/sisvga_crtc.c new file mode 100644 index 0000000000000..228c3c0a7f334 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_crtc.c @@ -0,0 +1,979 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane_helper.h> +#include "sisvga_reg.h" + +static struct sisvga_crtc* sisvga_crtc_of(struct drm_crtc* crtc) +{ + return container_of(crtc, struct sisvga_crtc, base); +} + +/* + * LUT helpers + */ + +static void load_lut_18(struct sisvga_device *sdev, const u8 (*lut)[3], + size_t len) +{ + size_t i, j; + u8 rgb[3]; + + for (i = 0; i < len; ++i) { + RREG_DAC(i, rgb[0], rgb[1], rgb[2]); + for (j = 0; j < ARRAY_SIZE(rgb); ++j) { + rgb[j] = (rgb[j] & 0xc0) | (lut[i][0] & 0x3f); + } + WREG_DAC(i, rgb[0], rgb[1], rgb[2]); + } +} + +/* SiS graphics cards allow for 8-bit values in the DAC. */ +static void load_lut_24(struct sisvga_device *sdev, const u8 (*lut)[3], + size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + WREG_DAC(i, lut[i][0], lut[i][1], lut[i][2]); + } +} + +/* + * DPMS helpers + */ + +static void set_crtc_dpms_mode(struct sisvga_device* sdev, int mode) +{ + u8 sr01; + + RREG_SR(0x01, sr01); + + switch (mode) { + case DRM_MODE_DPMS_ON: + sr01 &= 0xdf; /* screen enabled */ + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + sr01 |= 0x20; /* screen disabled */ + break; + default: + DRM_ERROR("sisvga: invalid DPMS mode %d\n", mode); + return; + } + + WREG_SR(0x01, sr01); +} + +/* + * Mode-setting helpers + */ + +static u32 bytes_per_pixel(const struct drm_format_info* format) +{ + u32 bpp; + u8 i; + + for (bpp = 0, i = 0; i < format->num_planes; ++i) { + bpp += format->cpp[i]; + } + return bpp; +} + +static int compute_offset(unsigned int width, + const struct drm_format_info* format, + u8 addr_incr, bool interlaced) +{ + int offset = width * bytes_per_pixel(format); + + if (interlaced) + offset *= 2; + + return offset / (addr_incr * 2); + +} + +static int set_framebuffer(struct sisvga_device *sdev, + struct sisvga_framebuffer *new_sis_fb, + struct sisvga_framebuffer *old_sis_fb, + bool atomic, bool interlaced) +{ + struct sisvga_bo *bo; + int ret, offset; + u64 gpu_addr; + u32 start_address; + u8 cr13, cr0c, cr0d, sr02, sr03, sr04, sr06, sr0a, sr0c, sr26, sr27, sr3e; + + /* push the previous fb to system ram */ + if (!atomic && old_sis_fb) { + bo = gem_to_sisvga_bo(old_sis_fb->gem_obj); + ret = sisvga_bo_reserve(bo, false); + if (ret) + return ret; + sisvga_bo_push_to_system(bo); + sisvga_bo_unreserve(bo); + } + + bo = gem_to_sisvga_bo(new_sis_fb->gem_obj); + + ret = sisvga_bo_reserve(bo, false); + if (ret) + return ret; + + ret = sisvga_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + sisvga_bo_unreserve(bo); + return ret; + } + + sisvga_bo_unreserve(bo); + + ret = compute_offset(new_sis_fb->base.width, new_sis_fb->base.format, + 4, interlaced); + if (ret < 0) + return ret; + offset = ret; + + start_address = gpu_addr >> 2; + + RREG_SR(0x02, sr02); + RREG_SR(0x03, sr03); + RREG_SR(0x04, sr04); + RREG_SR(0x06, sr06); + RREG_SR(0x0a, sr0a); + RREG_SR(0x27, sr27); + RREG_SR(0x3e, sr3e); + + cr13 = offset & 0xff; + cr0c = (start_address & 0x0000ff00) >> 8; + cr0d = start_address & 0x000000ff; + sr02 &= 0xf0; /* preserve reserved bits */ + sr02 |= 0x0f; /* writes to all planes enabled */ + sr03 &= 0xc0; /* preserve reserved bits */ + sr04 &= 0xf1; /* preserve reserved bits */ + sr04 |= 0x08 | /* Chain-4 mode enabled */ + 0x04 | /* Odd/Even disable */ + 0x02; /* Extended memory enabled */ + sr06 |= 0x80; /* linear addressing enabled */ + sr0a &= 0x0f; /* preserve bits */ + sr0a |= (offset & 0xf00) >> 4; + sr0c = 0x80; /* 32-bit graphics-memory access enabled */ + sr26 = 0x10; /* continuous memory access enabled */ + sr27 &= 0xf0; /* preserve reserved bits */ + sr27 |= (start_address & 0x000f0000) >> 16; + + WREG_CR(0x13, cr13); + WREG_CR(0x0c, cr0c); + WREG_CR(0x0d, cr0d); + + WREG_SR(0x02, sr02); + WREG_SR(0x03, sr03); + WREG_SR(0x04, sr04); + WREG_SR(0x06, sr06); + WREG_SR(0x0a, sr0a); + WREG_SR(0x0c, sr0c); + WREG_SR(0x26, sr26); + WREG_SR(0x27, sr27); + WREG_SR(0x3e, sr3e); + + return 0; +} + +static int set_color_mode(const struct sisvga_device *sdev, + const struct drm_framebuffer *fb) +{ + struct drm_format_name_buf buf; + u8 cr14, cr17, ar10, ar11, ar12, ar13, ar14, gr00, gr01, + gr02, gr03, gr04, gr05, gr06, gr07, gr08, sr06, sr0b; + + RREG_CR(0x14, cr14); + RREG_CR(0x17, cr17); + RREG_GR(0x00, gr00); + RREG_GR(0x01, gr01); + RREG_GR(0x02, gr02); + RREG_GR(0x03, gr03); + RREG_GR(0x04, gr04); + RREG_GR(0x05, gr05); + RREG_GR(0x06, gr06); + RREG_GR(0x07, gr07); + RREG_AR(0x10, ar10); + RREG_AR(0x12, ar12); + RREG_AR(0x13, ar13); + RREG_AR(0x14, ar14); + RREG_SR(0x06, sr06); + RREG_SR(0x0b, sr0b); + + cr14 |= 0x40; /* set double-word addressing */ + //cr14 &= 0x9f; /* double-word addressing disabled */ + cr17 &= 0xbf; /* word-mode addressing enabled */ + //if (fb->format->depth < 8) + // cr17 |= 0x20; + cr17 |= /*0x20 |*/ /* set MA 15 bit in word-address mode */ + 0x80; /* word-based refresh enabled */ + + gr00 &= 0xf0; /* preserve reserved bits */ + gr01 &= 0xf0; /* preserve reserved bits */ + gr02 &= 0xf0; /* preserve reserved bits */ + gr03 &= 0xe0; /* preserve reserved bits */ + gr04 &= 0xfa; /* preserve reserved bits */ + gr05 &= 0x84; /* preserve reserved bits */ + gr06 &= 0xf0; /* preserve reserved bits */ + gr06 |= /*0x04 |*/ + 0x01; /* graphics mode */ + gr07 &= 0xf0; /* preserve reserved bits */ + gr07 |= 0x0f; /* color-don't-care for Read Mode 1 */ + gr08 = 0xff; /* bitmask for Write Modes 0,2,3 */ + + ar10 &= 0x10; /* preserve reserved bits */ + ar10 |= 0x01; /* graphics mode enabled */ + ar11 = 0x00; /* clear overscan palette index */ + ar12 &= 0xf0; /* preserve reserved bits */ + //ar12 |= 0x0f; /* enable color planes */ + ar13 &= 0xf0; /* preserve reserved bits */ + ar14 &= 0xf0; /* preserve reserved bits */ + + /* In TrueColor mode, the hardware expects RGB buffers in + * big-endian byte order. If the framebuffer is in little + * endian, we invert the RGB/BGR mode. */ + switch (fb->format->format) { + case DRM_FORMAT_RGB888: + sr06 &= 0xe0; /* clear graphics/text mode */ + sr06 |= 0x10; /* TrueColor enabled */ + sr06 |= 0x02; /* enhanced graphcs mode enabled */ + if (fb->format->format & DRM_FORMAT_BIG_ENDIAN) + sr0b &= 0x7f; /* RGB byte order */ + else + sr0b |= 0x80; /* RGB byte order (little endian) */ + break; + case DRM_FORMAT_BGR888: + sr06 &= 0xe0; /* clear graphics/text mode */ + sr06 |= 0x10; /* TrueColor enabled */ + sr06 |= 0x02; /* enhanced graphcs mode enabled */ + if (fb->format->format & DRM_FORMAT_BIG_ENDIAN) + sr0b |= 0x80; /* BGR byte order */ + else + sr0b &= 0x7f; /* BGR byte order (little endian) */ + break; + case DRM_FORMAT_RGB565: + sr06 &= 0xe0; /* clear graphics/text mode */ + sr06 |= 0x08; /* 64KColor enabled */; + sr06 |= 0x02; /* enhanced graphcs mode enabled */ + sr0b &= 0x7f; /* clear RGB byte order */ + break; + case DRM_FORMAT_XRGB1555: + sr06 &= 0xe0; /* clear graphics/text mode */ + sr06 |= 0x04; /* 32KColor enabled */ + sr06 |= 0x02; /* enhanced graphcs mode enabled */ + sr0b &= 0x7f; /* clear RGB byte order */ + break; + case DRM_FORMAT_C8: + gr05 = 0x40; /* 256-color palette enabled */ + ar10 |= 0x40; /* 8-bit palette enabled */ + sr06 &= 0xe3; /* clear enhanced color modes */ + sr0b &= 0x7f; /* clear RGB byte-order bit */ + break; + default: /* unsupported VGA color configuration */ + /* BUG: We should have detected this in mode_fixup(). */ + DRM_ERROR("sisvga: %s color format is not supported\n", + drm_get_format_name(fb->format->format, &buf)); + return -EINVAL; + } + + WREG_CR(0x14, cr14); + WREG_CR(0x17, cr17); + + WREG_GR(0x00, gr00); + WREG_GR(0x01, gr01); + WREG_GR(0x02, gr02); + WREG_GR(0x03, gr03); + WREG_GR(0x04, gr04); + WREG_GR(0x05, gr05); + WREG_GR(0x06, gr06); + WREG_GR(0x07, gr07); + WREG_GR(0x08, gr08); + + WREG_AR(0x10, ar10); + WREG_AR(0x11, ar11); + WREG_AR(0x12, ar12); + WREG_AR(0x13, ar13); + WREG_AR(0x14, ar14); + + WREG_SR(0x06, sr06); + WREG_SR(0x0b, sr0b); + + return 0; +} + +static bool is_9_dot_mode(const struct drm_display_mode* mode) +{ + int hdisplay = mode->hdisplay; + if (mode->flags & DRM_MODE_FLAG_CLKDIV2) + hdisplay *= 2; + + /* VGA 9-dot modes have a ratio of 9:5 */ + return !(hdisplay % 9) && (((mode->vdisplay * 9) / 5) == hdisplay); +} + +static int set_display_mode(struct sisvga_device *sdev, + const struct drm_display_mode *mode) +{ + static const enum sisvga_freq freq_list[] = { + SISVGA_FREQ_14318, + SISVGA_FREQ_25175, + SISVGA_FREQ_28322 + }; + static const u8 freq_bits_list[] = { + [SISVGA_FREQ_14318] = 0x03, + [SISVGA_FREQ_25175] = 0x00, + [SISVGA_FREQ_28322] = 0x01 + }; + static const u8 freqi_bits_list[] = { + [SISVGA_FREQ_14318] = 0x00, + [SISVGA_FREQ_25175] = 0x01, + [SISVGA_FREQ_28322] = 0x02 + }; + + size_t i; + long ret; + enum sisvga_vclk vclk; + enum sisvga_freq freq; + unsigned long num, denum, div, postscal, f; + u8 freq_bits, freqi_bits, num_bits, denum_bits, div_bits, postscal_bits; + + int dots, htotal, hsync_start, hsync_end, hdisplay, hskew, hblank_start, + hblank_end, vtotal, vsync_start, vsync_end, vdisplay, vscan, + vblank_start, vblank_end, line_compare; + u8 misc; + u8 cr00, cr01, cr02, cr03, cr04, cr05, cr06, cr07, cr08, cr09, cr0a, + cr0b, cr0c, cr0d, cr0e, cr0f, cr10, cr11, cr12, cr14, cr15, + cr16, cr17, cr18; + u8 sr01, sr06, sr07, sr0a, sr12, sr13, sr2a, sr2b, sr38; + + /* The CRTC values are the same as for regular VGA adapters. + * Some of the bits for higher resolutions will be copied to + * SiS' extended registers. */ + + if (is_9_dot_mode(mode)) + dots = 9; + else + dots = 8; + + htotal = (mode->crtc_htotal / dots) - 5; + hsync_start = (mode->crtc_hsync_start / dots) - 1; + hsync_end = (mode->crtc_hsync_end / dots) - 1; + hdisplay = (mode->crtc_hdisplay / dots) - 1; + if (mode->flags & DRM_MODE_FLAG_HSKEW) + hskew = mode->hskew / dots; + else + hskew = 0; + hblank_start = (mode->crtc_hblank_start / dots) - 1; + hblank_end = (mode->crtc_hblank_end / dots) - 1; + + vtotal = mode->crtc_vtotal - 2; + vsync_start = mode->crtc_vsync_start - 1; + vsync_end = mode->crtc_vsync_end - 1; + vdisplay = mode->crtc_vdisplay - 1; + if (mode->vscan) + vscan = mode->vscan - 1; + else + vscan = 0; + vblank_start = mode->crtc_vblank_start - 1; + vblank_end = mode->crtc_vblank_end - 1; + line_compare = mode->crtc_vtotal + 1; /* beyond end of display; disabled */ + + /* We have to compute the PLL's configuration for the given + * dot clock. With the computed parameters, we can also select + * the correct registers. sr38 serves as index register for + * sr13, sr2a, and sr2b. */ + + ret = sisvga_vclk_of_clock(mode->clock, &vclk); + if (ret < 0) { + /* BUG: We should have detected this in mode_valid(). */ + DRM_ERROR("sisvga: unsupported dot clock of %d KHz, error %ld", + mode->clock, -ret); + return ret; + } + sisvga_vclk_regs(vclk, &freq, &num, &denum, &div, &postscal, &f); + + freq_bits = freq_bits_list[freq]; + freqi_bits = freqi_bits_list[freq]; + num_bits = num - 1; + denum_bits = denum - 1; + div_bits = div - 1; + postscal_bits = postscal - 1; + + misc = RREG8(REG_MISC_IN); + + RREG_CR(0x08, cr08); + RREG_CR(0x09, cr09); + RREG_CR(0x11, cr11); + RREG_CR(0x14, cr14); + RREG_CR(0x17, cr17); + + /* Below is the register setup code. sr38 contains the index + * register for sr13, sr2a and sr2b. We set it up before all + * other sequencer registers. With the correct registers + * selected we can later configure the dot clock generator. */ + /* TODO: NOT on 6202 ??? */ + RREG_SR(0x38, sr38); + sr38 &= 0xfc; /* clear clock-register selector */ + sr38 |= freqi_bits; + WREG_SR(0x38, sr38); + WAIT_SR_MASKED(0x38, freqi_bits, 0x03); + + RREG_SR(0x01, sr01); + RREG_SR(0x06, sr06); + RREG_SR(0x07, sr07); + if (MODEL_IS_GE(6326)) + RREG_SR(0x13, sr13); + else + sr13 = 0; + RREG_SR(0x0a, sr0a); + RREG_SR(0x2a, sr2a); + RREG_SR(0x2b, sr2b); + if (MODEL_IS_GE(6326)) + RREG_SR(0x38, sr38); + else + sr38 = 0; + + cr00 = htotal & 0xff; + cr01 = hdisplay & 0xff; + cr02 = hblank_start & 0xff; + cr03 = 0x80 | /* preserve reserved bit */ + ((hskew & 0x03) << 5) | + (hblank_end & 0x1f); + cr04 = hsync_start & 0xff; + cr05 = ((hblank_end & 0x20) << 2) | + (hsync_end & 0x1f); + cr06 = vtotal & 0xff; + cr07 = ((vsync_start & 0x200) >> 2) | + ((vdisplay & 0x200) >> 3) | + ((vtotal & 0x200) >> 4) | + ((line_compare & 0x100) >> 4) | + ((vblank_start & 0x100) >> 5) | + ((vsync_start & 0x100) >> 6) | + ((vdisplay & 0x100) >> 7) | + ((vtotal & 0x100) >> 8); + cr08 &= 0x80; /* preserve bit */ + cr09 = ((line_compare & 0x200) >> 3) | + ((vblank_start & 0x200) >> 4) | + (vscan & 0x1f); + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + cr09 |= 0x80; + cr0a = 0; + cr0b = 0; + cr0c = 0; + cr0d = 0; + cr0e = 0; + cr0f = 0; + cr10 = vsync_start & 0xff; + cr11 = vsync_end & 0x0f; + cr12 = vdisplay & 0xff; + cr14 &= 0x80; /* preserve reserved bit */ + cr15 = vblank_start & 0xff; + cr16 = vblank_end & 0xff; /* SiS uses all 8 bits */ + cr17 |= 0x40 | /* byte-address mode enabled */ + 0x03; + cr18 = line_compare & 0xff; + + if (dots == 9) + sr01 &= 0xfe; /* 9-dot mode */ + else + sr01 |= 0x01; /* 8-dot mode */ + sr0a &= 0xf0; /* preserve bits */ + sr0a |= ((vsync_start & 0x400) >> 7) | + ((vblank_start & 0x400) >> 8) | + ((vdisplay & 0x400) >> 9) | + ((vtotal & 0x400) >> 10); + if (MODEL_IS_GE(6326)) + sr12 = ((hblank_end & 0x40) >> 2) | + ((hsync_start & 0x100) >> 5) | + ((hblank_start & 0x100) >> 6) | + ((hdisplay & 0x100) >> 7) | + ((htotal & 0x100) >> 8); + else + sr12 = 0; + + misc &= 0x3f; /* clear sync bits */ + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + misc |= 0x80; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + misc |= 0x40; + + misc &= 0xf3; /* clear clock selector */ + misc |= freq_bits << 2; + + if (mode->flags & DRM_MODE_FLAG_CLKDIV2) + sr01 |= 0x08; + else + sr01 &= 0xf7; /* don't divide dot-clock rate by 2 */ + + /* TODO: Force 9/8 dot mode to 0 when switching to + * 25.175 MHz in rev 0b and earlier. */ + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + sr06 |= 0x20; + else + sr06 &= 0xdf; + + if (mode->clock > MHZ_TO_KHZ(135)) + sr07 |= 0x02; /* high-frequency DAC enabled */ + else + sr07 &= 0xfd; + + if (freq == SISVGA_FREQ_14318) { + + /* On all SiS adapters we have to configure the internal dot + * clock generator. In theory (aka 'the manual') on SiS 6326 + * we can configure VGA clock generators in the same way. In + * practice hardware doesn't support it. So we only use VGA + * registers for VGA dot clocks, and extended registers for + * the internal clock generator. */ + + if (MODEL_IS_GE(6326)) { + if (postscal_bits & 0x04) + sr13 |= 0x40; + else + sr13 &= 0xbf; /* clear post-scaler bit */ + } + + sr2a = ((div_bits & 0x01) << 7) | + (num_bits & 0x7f); + sr2b = ((postscal_bits & 0x03) << 5) | + (denum_bits & 0x1f); + if (mode->clock > MHZ_TO_KHZ(135)) + sr2b |= 0x80; /* high-frequency gain enabled */ + + sr06 |= 0x03; /* enable enhanced modes */ + } else { + sr06 &= 0xfc; /* disable enhanced modes */ + } + + if (MODEL_IS_GE(6326)) + sr38 |= 0x04; /* Disable line compare */ + + WREG8(REG_MISC_OUT, misc); + WAIT8_MASKED(REG_MISC_IN, misc, 0xef); + + WREG_SR(0x01, sr01); + + /* VCLK setup */ + if (freq == SISVGA_FREQ_14318) { + if (MODEL_IS_GE(6326)) + WREG_SR(0x13, sr13); + WREG_SR(0x2a, sr2a); + WREG_SR(0x2b, sr2b); + } + + WREG_SR(0x0a, sr0a); + WREG_SR(0x06, sr06); + WREG_SR(0x07, sr07); + if (MODEL_IS_GE(6326)) { + WREG_SR(0x12, sr12); + WREG_SR(0x38, sr38); + } + + WREG_CR(0x00, cr00); + WREG_CR(0x01, cr01); + WREG_CR(0x02, cr02); + WREG_CR(0x03, cr03); + WREG_CR(0x04, cr04); + WREG_CR(0x05, cr05); + WREG_CR(0x06, cr06); + WREG_CR(0x07, cr07); + WREG_CR(0x08, cr08); + WREG_CR(0x09, cr09); + WREG_CR(0x0a, cr0a); + WREG_CR(0x0b, cr0b); + WREG_CR(0x0c, cr0c); + WREG_CR(0x0d, cr0d); + WREG_CR(0x0e, cr0e); + WREG_CR(0x0f, cr0f); + WREG_CR(0x10, cr10); + WREG_CR(0x11, cr11); + WREG_CR(0x12, cr12); + WREG_CR(0x14, cr14); + WREG_CR(0x15, cr15); + WREG_CR(0x16, cr16); + WREG_CR(0x17, cr17); + WREG_CR(0x18, cr18); + + return 0; +} + +/* + * CRTC helper funcs + */ + +static void sisvga_crtc_helper_disable(struct drm_crtc *crtc) +{ + set_crtc_dpms_mode(crtc->dev->dev_private, DRM_MODE_DPMS_OFF); +} + +static void sisvga_crtc_helper_dpms(struct drm_crtc *crtc, int mode) +{ + struct sisvga_crtc *sis_crtc = sisvga_crtc_of(crtc); + + set_crtc_dpms_mode(crtc->dev->dev_private, mode); + + if (sis_crtc->lut_24) { + load_lut_24(crtc->dev->dev_private, sis_crtc->lut, + ARRAY_SIZE(sis_crtc->lut)); + } else { + load_lut_18(crtc->dev->dev_private, sis_crtc->lut, + ARRAY_SIZE(sis_crtc->lut)); + } +} + +static bool sisvga_crtc_helper_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + u8 bpp; + int framebuffer_size; + struct sisvga_device *sdev = crtc->dev->dev_private; + const struct sisvga_device_info *info = sdev->info; + + if (adj_mode->hdisplay > info->max_hdisplay) + return false; + + if (adj_mode->vdisplay > info->max_vdisplay) + return false; + + if (((adj_mode->crtc_hdisplay % 8) != 0 || + (adj_mode->crtc_hsync_start % 8) != 0 || + (adj_mode->crtc_hsync_end % 8) != 0 || + (adj_mode->crtc_htotal % 8) != 0) && + ((adj_mode->crtc_hdisplay % 9) != 0 || + (adj_mode->crtc_hsync_start % 9) != 0 || + (adj_mode->crtc_hsync_end % 9) != 0 || + (adj_mode->crtc_htotal % 9) != 0)) { + return false; + } + + if (adj_mode->crtc_hdisplay > info->max_hdisplay || + adj_mode->crtc_hsync_start > info->max_hsync_start || + adj_mode->crtc_hsync_end > info->max_hsync_end || + adj_mode->crtc_htotal > info->max_htotal || + adj_mode->crtc_vdisplay > info->max_vdisplay || + adj_mode->crtc_vsync_start > info->max_vsync_start || + adj_mode->crtc_vsync_end > info->max_vsync_end || + adj_mode->crtc_vtotal > info->max_vtotal) { + return false; + } + + bpp = bytes_per_pixel(crtc->primary->fb->format); + framebuffer_size = adj_mode->crtc_hdisplay * adj_mode->crtc_vdisplay * + bpp; + + if (framebuffer_size > sdev->vram.size) + return false; + + return true; +} + +static int sisvga_crtc_helper_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + int ret; + + ret = set_display_mode(crtc->dev->dev_private, adj_mode); + if (ret < 0) + return ret; + + ret = set_color_mode(crtc->dev->dev_private, crtc->primary->fb); + if (ret < 0) + return ret; + + ret = set_framebuffer(crtc->dev->dev_private, + sisvga_framebuffer_of(crtc->primary->fb), + sisvga_framebuffer_of(old_fb), + false, + !!(mode->flags & DRM_MODE_FLAG_INTERLACE)); + if (ret < 0) + return ret; + + { + int len = 3 * crtc->primary->fb->width * crtc->primary->fb->height; + struct sisvga_device* sdev = crtc->dev->dev_private; + for (ret = 0; ret < len; ++ret) + ((u8*)sdev->vram.mem)[ret] = 0xff; + } + + return 0; +} + +static int sisvga_crtc_helper_mode_set_base(struct drm_crtc *crtc, + int x, int y, + struct drm_framebuffer *old_fb) +{ + int ret; + + ret = set_framebuffer(crtc->dev->dev_private, + sisvga_framebuffer_of(crtc->primary->fb), + sisvga_framebuffer_of(old_fb), + false, false); + if (ret < 0) + return ret; + + return 0; +} + +static void sisvga_crtc_helper_prepare(struct drm_crtc *crtc) +{ + struct sisvga_device* sdev = crtc->dev->dev_private; + u8 sr00, sr01; + + /* We disable the screen to allow for flicker-free + * mode switching. */ + set_crtc_dpms_mode(crtc->dev->dev_private, DRM_MODE_DPMS_OFF); + + RREG_SR(0x00, sr00); + RREG_SR(0x01, sr01); + + sr00 &= 0xfc; /* sequencer reset */ + sr01 |= 0x20; /* screen disabled */ + + WREG_SR(0x01, sr01); + WREG_SR(0x00, sr00); +} + +static void sisvga_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct sisvga_device* sdev = crtc->dev->dev_private; + u8 sr00, sr01; + + RREG_SR(0x00, sr00); + RREG_SR(0x01, sr01); + + sr00 |= 0x03; /* no reset; start sequencer */ + sr01 &= 0xdf; /* screen enabled */ + + WREG_SR(0x00, sr00); + WREG_SR(0x01, sr01); + + set_crtc_dpms_mode(crtc->dev->dev_private, DRM_MODE_DPMS_ON); + +} + +static const struct drm_crtc_helper_funcs sisvga_crtc_helper_funcs = { + .disable = sisvga_crtc_helper_disable, + .dpms = sisvga_crtc_helper_dpms, + .mode_fixup = sisvga_crtc_helper_mode_fixup, + .mode_set = sisvga_crtc_helper_mode_set, + .mode_set_base = sisvga_crtc_helper_mode_set_base, + .prepare = sisvga_crtc_helper_prepare, + .commit = sisvga_crtc_helper_commit, +}; + +/* + * CRTC funcs + */ + +static int sisvga_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, + uint32_t height) +{ + return 0; +} + +static int sisvga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + return 0; +} + +static int sisvga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + uint32_t i; + struct sisvga_crtc *sis_crtc = sisvga_crtc_of(crtc); + + for (i = 0; i < size; i++) { + sis_crtc->lut[i][0] = red[i] >> 8; + sis_crtc->lut[i][1] = green[i] >> 8; + sis_crtc->lut[i][2] = blue[i] >> 8; + } + sis_crtc->lut_len = size; + sis_crtc->lut_24 = true; + load_lut_24(crtc->dev->dev_private, sis_crtc->lut, sis_crtc->lut_len); + + return 0; +} + +static void sisvga_crtc_destroy(struct drm_crtc *crtc) +{ + struct sisvga_crtc *sis_crtc = sisvga_crtc_of(crtc); + struct drm_device *dev = crtc->dev; + + drm_crtc_cleanup(&sis_crtc->base); + devm_kfree(dev->dev, sis_crtc); +} + +static int sisvga_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event, + uint32_t flags, + struct drm_modeset_acquire_ctx *ctx) +{ + struct sisvga_device *sdev = crtc->dev->dev_private; + struct drm_framebuffer *old_fb = crtc->primary->fb; + unsigned long irqflags; + struct drm_format_name_buf buf; + + crtc->primary->fb = fb; + sisvga_crtc_helper_mode_set_base(crtc, 0, 0, old_fb); + + if (event) { + spin_lock_irqsave(&sdev->base.event_lock, irqflags); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irqrestore(&sdev->base.event_lock, irqflags); + } + + return 0; +} + +static const struct drm_crtc_funcs sisvga_crtc_funcs = { + .cursor_set = sisvga_crtc_cursor_set, + .cursor_move = sisvga_crtc_cursor_move, + .gamma_set = sisvga_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = sisvga_crtc_destroy, + .page_flip = sisvga_crtc_page_flip, +}; + +/* + * struct sisvga_crtc + */ + +static int map_crtc_registers(struct sisvga_device *sdev) +{ + static const u8 mask = 0x01; + u8 newmisc, misc; + int i = 0; + + /* CRTC registers can be mapped in two separate locations: 0x3b? for + * compatibility with monochrome adapters and 0x3d? for compatibility + * with color adapters. We always use the latter. The loop waits + * several microseconds for success if neccessary. */ + + misc = RREG8(REG_MISC_IN); + + newmisc = misc | 0x01; + + do { + if ((misc & mask) == (newmisc & mask)) + return 0; + if (i) + udelay(10); + else + WREG8(REG_MISC_OUT, newmisc); + misc = RREG8(REG_MISC_IN); + } while (i++ < 5); + + DRM_ERROR("sisvga: failed to map CRTC registers\n"); + + return -ETIMEDOUT; +} + +static int unlock_crtc_registers(struct sisvga_device *sdev) +{ + u8 cr11; + int i = 0; + + /* Some of the CRTC registers might be write protected. We unprotect + * them here, or assume the device is not compatible. The loop waits + * several microseconds for success if necessary.*/ + + RREG_CR(0x11, cr11); + + do { + if (!(cr11 & 0x80)) + return 0; + if (i) + udelay(10); + else + WREG_CR(0x11, cr11 & 0x7f); + RREG_CR(0x11, cr11); + } while (i++ < 5); + + DRM_ERROR("sisvga: failed to disable CRTC write protection\n"); + + return -ETIMEDOUT; +} + +static int sisvga_crtc_init(struct sisvga_crtc *sis_crtc, + struct drm_device *dev, + struct drm_plane *primary_plane, + struct drm_plane *cursor_plane) +{ + struct sisvga_device *sdev = dev->dev_private; + int ret; + size_t i, j; + + ret = map_crtc_registers(sdev); + if (ret < 0) + return ret; + ret = unlock_crtc_registers(sdev); + if (ret < 0) + return ret; + + ret = drm_crtc_init_with_planes(dev, &sis_crtc->base, + primary_plane, cursor_plane, + &sisvga_crtc_funcs, NULL); + if (ret < 0) { + DRM_ERROR("sisvga: drmcrtc_init_with_planes() failed," + " error %d\n", -ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(sis_crtc->lut); ++i) { + for (j = 0; j < ARRAY_SIZE(sis_crtc->lut[0]); ++j) { + sis_crtc->lut[i][j] = i; + } + } + sis_crtc->lut_len = ARRAY_SIZE(sis_crtc->lut); + sis_crtc->lut_24 = true; + drm_mode_crtc_set_gamma_size(&sis_crtc->base, sis_crtc->lut_len); + + drm_crtc_helper_add(&sis_crtc->base, &sisvga_crtc_helper_funcs); + + return 0; +} + +struct sisvga_crtc* sisvga_crtc_create(struct drm_device *dev, + struct drm_plane *primary_plane, + struct drm_plane *cursor_plane) +{ + struct sisvga_crtc* sis_crtc; + int ret; + + sis_crtc = devm_kzalloc(dev->dev, sizeof(*sis_crtc), GFP_KERNEL); + if (!sis_crtc) + return ERR_PTR(-ENOMEM); + + ret = sisvga_crtc_init(sis_crtc, dev, primary_plane, cursor_plane); + if (ret) + goto err_sisvga_crtc_init; + + return sis_crtc; + +err_sisvga_crtc_init: + devm_kfree(dev->dev, sis_crtc); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_ddc.c b/drivers/gpu/drm/sisvga/sisvga_ddc.c new file mode 100644 index 0000000000000..f41f12a075efb --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_ddc.c @@ -0,0 +1,411 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + * + * Based on regular I2C algorithm as implemented in + * + * drivers/i2c/algos/i2c-algo-bit.c + * + * DO NOT COPY-AND-PASTE THIS FILE INTO YOUR DRM DRIVER! SiS graphics + * cards use their own variant of the i2c bit-shift algorithm for DDC. + * Using the SiS algorithm can damage your hardware. + */ + +#include "sisvga_device.h" +#include <drm/drm_device.h> +#include <linux/timex.h> +#include "sisvga_reg.h" + +/* + * I2C helpers + */ + +static int i2c_get(struct sisvga_device *sdev, u8 mask) +{ + u8 sr11; + + BUG_ON(!sdev); + RREG_SR(0x11, sr11); + + return (sr11 & mask) != 0; +} + +static void i2c_set(struct sisvga_device *sdev, u8 mask, int state) +{ + u8 sr11; + + BUG_ON(!sdev); + RREG_SR(0x11, sr11); + + if (state) + sr11 |= mask; + else + sr11 &= ~mask; + + WREG_SR(0x11, sr11); +} + +/* + * SiS DDC I2C algorithm + */ + +static int getsda(struct sisvga_ddc *sis_ddc) +{ + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + return i2c_get(sdev, sis_ddc->sda_mask); +} + +static void setsda(struct sisvga_ddc *sis_ddc, int state) +{ + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + i2c_set(sdev, sis_ddc->sda_mask, state); +} + +static int getscl(struct sisvga_ddc *sis_ddc) +{ + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + return i2c_get(sdev, sis_ddc->scl_mask); +} + +static void setscl(struct sisvga_ddc *sis_ddc, int state) +{ + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + i2c_set(sdev, sis_ddc->scl_mask, state); +} + +static int setscl_validate(struct sisvga_ddc *sis_ddc, int state) +{ + unsigned long timeout; + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + i2c_set(sdev, sis_ddc->scl_mask, state); + + timeout = jiffies + sis_ddc->timeout; + while (getscl(sis_ddc) != state) { + if (time_after(jiffies, timeout)) { + if (getscl(sis_ddc) == state) + break; + DRM_ERROR("SCL state validation failed with timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + udelay(sis_ddc->udelay); + + return 0; +} + +static int pre_xfer(struct sisvga_ddc *sis_ddc) +{ + int ret; + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + /* enable display while probing EDID */ + RREG_SR(0x01, sis_ddc->sr01); + if (!(sis_ddc->sr01 & 0x20)) + WREG_SR(0x01, sis_ddc->sr01 & ~0x20); + + /* raise SDA, SCL before transfer */ + setsda(sis_ddc, 1); + udelay((sis_ddc->udelay + 1) / 2); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void post_xfer(struct sisvga_ddc *sis_ddc) +{ + u8 sr01; + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + + /* restore register state */ + + RREG_SR(0x01, sr01); + if (sr01 != sis_ddc->sr01) + WREG_SR(0x01, sis_ddc->sr01); +} + +static int tx_start(struct sisvga_ddc *sis_ddc) +{ + /* expect SDA, SCL high */ + + setsda(sis_ddc, 0); + udelay(sis_ddc->udelay); + setscl(sis_ddc, 0); + + return 0; +} + +static int tx_repstart(struct sisvga_ddc *sis_ddc) +{ + int ret; + + setsda(sis_ddc, 1); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + + return tx_start(sis_ddc); +} + +static int tx_stop(struct sisvga_ddc *sis_ddc) +{ + int ret; + + /* expect SCL low */ + + setsda(sis_ddc, 0); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + setsda(sis_ddc, 1); + udelay(sis_ddc->udelay); + + return 0; +} + +static int tx_ack(struct sisvga_ddc *sis_ddc, u8 ack) +{ + int ret; + + /* expect SCL low */ + + setsda(sis_ddc, !ack); /* bit is invert to ack */ + udelay((sis_ddc->udelay + 1) / 2); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + setscl(sis_ddc, 0); + + return 0; +} + +static int rx_ack(struct sisvga_ddc *sis_ddc) +{ + int ret, sda; + + /* expect SCL low */ + + setsda(sis_ddc, 1); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + sda = getsda(sis_ddc); + setscl(sis_ddc, 0); + if (sda) + return -EPROTO; + + return 0; +} + +static int rx_byte(struct sisvga_ddc *sis_ddc, bool ack) +{ + int i, ret, sda; + u8 byte = 0; + + /* expect SCL low */ + + for (i = 0; i < 8; ++i) { + byte <<= 1; + /* In contrast to the regular I2C bit-transfer algorithm, we + * raise SDA before receiving each bit. Usually this would be + * done by the sender side. + * + * DO NOT COPY-AND-PASTE THIS CODE INTO YOUR DRM DRIVER! + */ + setsda(sis_ddc, 1); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + goto err; + sda = getsda(sis_ddc); + if (sda) + byte |= 0x01; + if (1) { + setscl(sis_ddc, 0); + udelay(i == 7 ? sis_ddc->udelay / 2 : sis_ddc->udelay); + } else { + struct sisvga_device *sdev = sis_ddc->dev->dev_private; + i2c_set(sdev, sis_ddc->scl_mask, 0); + udelay(i == 7 ? sis_ddc->udelay / 2 : sis_ddc->udelay); + } + } + ret = tx_ack(sis_ddc, ack); + if (ret < 0) + return ret; + + return byte; + +err: + setscl(sis_ddc, 0); + tx_ack(sis_ddc, !ack); + return ret; +} + +static int rx_buf(struct sisvga_ddc *sis_ddc, u8 *buf, u16 len) +{ + int l, ret; + + for (l = len; l--; ++buf) { + ret = rx_byte(sis_ddc, l); /* no ack on final byte */ + if (ret < 0) + return ret; + *buf = ret; + } + + return len; +} + +static int tx_byte(struct sisvga_ddc *sis_ddc, u8 byte) +{ + int i, ret; + + /* expect SCL low */ + + for (i = 0; i < 8; ++i) { + setsda(sis_ddc, byte & 0x80); + udelay((sis_ddc->udelay + 1) / 2); + ret = setscl_validate(sis_ddc, 1); + if (ret < 0) + return ret; + byte <<= 1; + setscl(sis_ddc, 0); + } + ret = rx_ack(sis_ddc); + if (ret < 0) + return ret; + + return 0; +} + +static int tx_buf(struct sisvga_ddc *sis_ddc, const u8 *buf, u16 len) +{ + int l, ret; + + for (l = len; l--; ++buf) { + ret = tx_byte(sis_ddc, *buf); + if (ret < 0) + return ret; + } + + return len; +} + +static int tx_addr(struct sisvga_ddc *sis_ddc, u8 addr, bool rx) +{ + return tx_byte(sis_ddc, (addr << 1) | !!rx); +} + +/* + * I2C adapter funcs + */ + +static int master_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + int ret, i; + struct sisvga_ddc *sis_ddc = i2c_get_adapdata(adapter); + + ret = pre_xfer(sis_ddc); + if (ret < 0) + return ret; + + ret = tx_start(sis_ddc); + if (ret < 0) + goto err_tx_start; + + for (i = 0; i < num; ++i) { + if (i) { + ret = tx_repstart(sis_ddc); + if (ret < 0) + goto err_msg; + } + if (msgs[i].flags & I2C_M_RD) { + ret = tx_addr(sis_ddc, msgs[i].addr, true); + if (ret < 0) + goto err_msg; + + ret = rx_buf(sis_ddc, msgs[i].buf, msgs[i].len); + if (ret < 0) + goto err_msg; + } else { + ret = tx_addr(sis_ddc, msgs[i].addr, false); + if (ret < 0) + goto err_msg; + + ret = tx_buf(sis_ddc, msgs[i].buf, msgs[i].len); + if (ret < 0) + goto err_msg; + } + } + + tx_stop(sis_ddc); + + post_xfer(sis_ddc); + + return i; /* number of messages signals success */ + +err_msg: + setscl(sis_ddc, 0); + tx_stop(sis_ddc); +err_tx_start: + post_xfer(sis_ddc); + return ret; + +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm sisvga_i2c_algorithm = { + .master_xfer = master_xfer, + .functionality = functionality, +}; + +/* + * struct sisvga_ddc + */ + +int sisvga_ddc_init(struct sisvga_ddc *sis_ddc, struct drm_device *dev) +{ + int ret; + + sis_ddc->dev = dev; + + sis_ddc->sda_mask = 0x02; + sis_ddc->scl_mask = 0x01; + + sis_ddc->udelay = 10; + sis_ddc->timeout = usecs_to_jiffies(2200); + + sis_ddc->adapter.owner = THIS_MODULE; + sis_ddc->adapter.class = I2C_CLASS_DDC; + sis_ddc->adapter.dev.parent = &dev->pdev->dev; + sis_ddc->adapter.algo = &sisvga_i2c_algorithm; + sis_ddc->adapter.algo_data = sis_ddc; + sis_ddc->adapter.retries = 3; + i2c_set_adapdata(&sis_ddc->adapter, sis_ddc); + snprintf(sis_ddc->adapter.name, sizeof(sis_ddc->adapter.name), + "sisvga DDC"); + + ret = i2c_add_adapter(&sis_ddc->adapter); + if (ret < 0) + return ret; + + return 0; +} + +void sisvga_ddc_fini(struct sisvga_ddc *sis_ddc) +{ + i2c_del_adapter(&sis_ddc->adapter); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_debug.c b/drivers/gpu/drm/sisvga/sisvga_debug.c new file mode 100644 index 0000000000000..1d11459d54e79 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_debug.c @@ -0,0 +1,303 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_debug.h" +#include "sisvga_device.h" +#include "sisvga_reg.h" + +void sisvga_debug_print_mode(struct sisvga_device* sdev) +{ + struct drm_display_mode mode; + + int dots, htotal, hsync_start, hsync_end, hdisplay, hskew, hblank_start, + hblank_end, vtotal, vsync_start, vsync_end, vdisplay, vscan, + vblank_start, vblank_end; + int fr, fd, num, denum, div, postscal; + + u8 misc; + u8 sr01, sr05, sr12, sr13, sr0a, sr2a, sr2b, sr38; + u8 cr00, cr01, cr02, cr03, cr04, cr05, cr06, cr07, cr09, + cr10, cr11, cr12, cr15, cr16; + u8 saved_sr38, freqi; + + misc = RREG8(REG_MISC_IN); + + RREG_SR(0x01, sr01); + RREG_SR(0x05, sr05); + RREG_SR(0x12, sr12); + RREG_SR(0x0a, sr0a); + + RREG_CR(0x00, cr00); + RREG_CR(0x01, cr01); + RREG_CR(0x02, cr02); + RREG_CR(0x03, cr03); + RREG_CR(0x04, cr04); + RREG_CR(0x05, cr05); + RREG_CR(0x06, cr06); + RREG_CR(0x07, cr07); + RREG_CR(0x09, cr09); + RREG_CR(0x10, cr10); + RREG_CR(0x11, cr11); + RREG_CR(0x12, cr12); + RREG_CR(0x15, cr15); + RREG_CR(0x16, cr16); + + if (sr01 & 0x01) + dots = 8; + else + dots = 9; + + htotal = (((u32)sr12 & 0x01) << 8) | cr00; + hblank_start = (((u32)sr12 & 0x04) << 6) | cr02; + hblank_end = (((u32)sr12 & 0x10) << 4) | + (((u32)sr05 & 0x80) >> 2) | (cr03 & 0x1f); + hdisplay = (((u32)sr12 & 0x02) << 7) | cr01; + hsync_start = (((u32)sr12 & 0x08) << 5) | cr04; + hsync_end = (((u32)sr12 & 0x08) << 5) | (cr04 & 0xe0) | (cr05 & 0x1f); + hskew = ((cr05 & 0x60) >> 5); + + vtotal = (((u32)sr0a & 0x01) << 9) | + (((u32)cr07 & 0x20) << 4) | + (((u32)cr07 & 0x01) << 8) | cr06; + vblank_start = (((u32)sr0a & 0x04) << 7) | + (((u32)cr09 & 0x20) << 4) | + (((u32)cr07 & 0x08) << 5) | cr15; + vblank_end = vblank_start + (cr16 & 0x1f); + vdisplay = (((u32)sr0a & 0x02) << 8) | + (((u32)cr07 & 0x02) << 7) | + (((u32)cr07 & 0x40) << 3) | cr12; + vsync_start = (((u32)sr0a & 0x08) << 6) | + (((u32)cr07 & 0x80) << 2) | + (((u32)cr07 & 0x04) << 6) | cr10; + vsync_end = (((u32)sr0a & 0x08) << 6) | + (((u32)cr07 & 0x80) << 2) | + (((u32)cr07 & 0x04) << 6) | (cr10 & 0xf0) | (cr11 & 0x0f); + vscan = cr09 & 0x1f; + + memset(&mode, 0, sizeof(mode)); + mode.type = DRM_MODE_TYPE_DRIVER; + mode.htotal = (htotal + 5) * dots; + /*mode.hblank_start = (hblank_start + 1) * dots; + mode.hblank_end = (hblank_end + 1) * dots;*/ + mode.hdisplay = (hdisplay + 1) * dots; + mode.hsync_start = (hsync_start + 1) * dots; + mode.hsync_end = (hsync_end + 1) * dots; + mode.hskew = hskew * dots; + + mode.vtotal = vtotal + 2; // 2? + /*mode.vblank_start = vblank_start; + mode.vblank_end = vblank_end;*/ + mode.vdisplay = vdisplay + 1; + mode.vsync_start = vsync_start + 1; + mode.vsync_end = vsync_end + 1; + if (vscan) + mode.vscan = vscan + 1; + else + mode.vscan = 0; + + + if (misc & 0x80) + mode.flags |= DRM_MODE_FLAG_NVSYNC; + else + mode.flags |= DRM_MODE_FLAG_PVSYNC; + if (misc & 0x40) + mode.flags |= DRM_MODE_FLAG_NHSYNC; + else + mode.flags |= DRM_MODE_FLAG_PHSYNC; + if (cr09 & 0x80) + mode.flags |= DRM_MODE_FLAG_DBLSCAN; + if (hskew) + mode.flags |= DRM_MODE_FLAG_HSKEW; + if (sr01 & 0x08) + mode.flags |= DRM_MODE_FLAG_CLKDIV2; + + RREG_SR(0x38, sr38); + + saved_sr38 = sr38; + + switch (misc & 0x0c) { + case 0x00: + fr = 25175; + freqi = 0x01; + break; + case 0x04: + fr = 28322; + freqi = 0x02; + break; + case 0x0c: + fr = 14813; + freqi = 0x00; + break; + default: + BUG(); + break; + } + + sr38 &= 0xfc; + sr38 |= freqi; + + WREG_SR(0x38, sr38); + WAIT_SR_MASKED(0x38, freqi, 0x03); + + RREG_SR(0x13, sr13); + RREG_SR(0x2a, sr2a); + RREG_SR(0x2b, sr2b); + + /* Restore previous sr38 */ + WREG_SR(0x38, saved_sr38); + WAIT_SR(0x38, saved_sr38); + + div = ((sr2a & 0x80) >> 7) + 1; + num = (sr2a & 0x7f) + 1; + denum = ((sr2b & 0x1f) + 1); + postscal = ((sr2b & 0x60) >> 5) + 1; + if (sr13 & 0x80) + postscal *= 2; + + fd = (fr * num * div) / (denum * postscal); + mode.clock = fd; + + DRM_INFO("fd=%d fr=%d num=%d denum=%d div=%d postscal=%d\n", fd, fr, num, denum, div, postscal); + + DRM_INFO("mode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&mode)); +} + +void sisvga_debug_print_regs(struct sisvga_device* sdev) +{ + u8 ar10, ar11, ar12, ar13, ar14; + u8 misc; + u8 sr00, sr01, sr03, sr04, sr06, sr0a, sr0b, sr0c, sr0d, + sr0e, sr0f, sr13, sr21, sr26, sr2a, sr2b, sr2d, sr38; + u8 gr00, gr01, gr02, gr03, gr04, gr05, gr06; + u8 cr00, cr01, cr02, cr03, cr04, cr05, cr06, cr07, + cr08, cr09, cr0a, cr0b, cr0c, cr0d, cr0e, cr0f, + cr10, cr11, cr12, cr13, cr14, cr15, cr16, cr17, + cr18; + + RREG_AR(0x10, ar10); + RREG_AR(0x11, ar11); + RREG_AR(0x12, ar12); + RREG_AR(0x13, ar13); + RREG_AR(0x14, ar14); + + misc = RREG8(REG_MISC_IN); + + RREG_SR(0x00, sr00); + RREG_SR(0x01, sr01); + RREG_SR(0x03, sr03); + RREG_SR(0x04, sr04); + RREG_SR(0x06, sr06); + RREG_SR(0x0a, sr0a); + RREG_SR(0x0b, sr0b); + RREG_SR(0x0c, sr0c); + RREG_SR(0x0d, sr0d); + RREG_SR(0x0e, sr0e); + RREG_SR(0x0f, sr0f); + RREG_SR(0x13, sr13); + RREG_SR(0x21, sr21); + RREG_SR(0x26, sr26); + RREG_SR(0x2a, sr2a); + RREG_SR(0x2b, sr2b); + RREG_SR(0x2d, sr2d); + RREG_SR(0x38, sr38); + + RREG_GR(0x00, gr00); + RREG_GR(0x01, gr01); + RREG_GR(0x02, gr02); + RREG_GR(0x03, gr03); + RREG_GR(0x04, gr04); + RREG_GR(0x05, gr05); + RREG_GR(0x06, gr06); + + RREG_CR(0x00, cr00); + RREG_CR(0x01, cr01); + RREG_CR(0x02, cr02); + RREG_CR(0x03, cr03); + RREG_CR(0x04, cr04); + RREG_CR(0x05, cr05); + RREG_CR(0x06, cr06); + RREG_CR(0x07, cr07); + RREG_CR(0x08, cr08); + RREG_CR(0x09, cr09); + RREG_CR(0x0a, cr0a); + RREG_CR(0x0b, cr0b); + RREG_CR(0x0c, cr0c); + RREG_CR(0x0d, cr0d); + RREG_CR(0x0e, cr0e); + RREG_CR(0x0f, cr0f); + RREG_CR(0x10, cr10); + RREG_CR(0x11, cr11); + RREG_CR(0x12, cr12); + RREG_CR(0x13, cr13); + RREG_CR(0x14, cr14); + RREG_CR(0x15, cr15); + RREG_CR(0x16, cr16); + RREG_CR(0x17, cr17); + RREG_CR(0x18, cr18); + + DRM_INFO("ar10=0x%x\n", ar10); + DRM_INFO("ar11=0x%x\n", ar11); + DRM_INFO("ar12=0x%x\n", ar12); + DRM_INFO("ar13=0x%x\n", ar13); + DRM_INFO("ar14=0x%x\n", ar14); + + DRM_INFO("misc=0x%x\n", misc); + + DRM_INFO("sr00=0x%x\n", sr00); + DRM_INFO("sr01=0x%x\n", sr01); + DRM_INFO("sr03=0x%x\n", sr03); + DRM_INFO("sr04=0x%x\n", sr04); + DRM_INFO("sr06=0x%x\n", sr06); + DRM_INFO("sr0a=0x%x\n", sr0a); + DRM_INFO("sr0b=0x%x\n", sr0b); + DRM_INFO("sr0c=0x%x\n", sr0c); + DRM_INFO("sr0d=0x%x\n", sr0d); + DRM_INFO("sr0e=0x%x\n", sr0e); + DRM_INFO("sr0f=0x%x\n", sr0f); + DRM_INFO("sr13=0x%x\n", sr13); + DRM_INFO("sr21=0x%x\n", sr21); + DRM_INFO("sr26=0x%x\n", sr26); + DRM_INFO("sr2a=0x%x\n", sr2a); + DRM_INFO("sr2b=0x%x\n", sr2b); + DRM_INFO("sr2d=0x%x\n", sr2d); + DRM_INFO("sr38=0x%x\n", sr38); + + DRM_INFO("gr00=0x%x\n", gr00); + DRM_INFO("gr01=0x%x\n", gr01); + DRM_INFO("gr02=0x%x\n", gr02); + DRM_INFO("gr03=0x%x\n", gr03); + DRM_INFO("gr04=0x%x\n", gr04); + DRM_INFO("gr05=0x%x\n", gr05); + DRM_INFO("gr06=0x%x\n", gr06); + + DRM_INFO("cr00=0x%x\n", cr00); + DRM_INFO("cr01=0x%x\n", cr01); + DRM_INFO("cr02=0x%x\n", cr02); + DRM_INFO("cr03=0x%x\n", cr03); + DRM_INFO("cr04=0x%x\n", cr04); + DRM_INFO("cr05=0x%x\n", cr05); + DRM_INFO("cr06=0x%x\n", cr06); + DRM_INFO("cr07=0x%x\n", cr07); + DRM_INFO("cr08=0x%x\n", cr08); + DRM_INFO("cr09=0x%x\n", cr09); + DRM_INFO("cr0a=0x%x\n", cr0a); + DRM_INFO("cr0b=0x%x\n", cr0b); + DRM_INFO("cr0c=0x%x\n", cr0c); + DRM_INFO("cr0d=0x%x\n", cr0d); + DRM_INFO("cr0e=0x%x\n", cr0e); + DRM_INFO("cr0f=0x%x\n", cr0f); + DRM_INFO("cr10=0x%x\n", cr10); + DRM_INFO("cr11=0x%x\n", cr11); + DRM_INFO("cr12=0x%x\n", cr12); + DRM_INFO("cr13=0x%x\n", cr13); + DRM_INFO("cr14=0x%x\n", cr14); + DRM_INFO("cr15=0x%x\n", cr15); + DRM_INFO("cr16=0x%x\n", cr16); + DRM_INFO("cr17=0x%x\n", cr17); + DRM_INFO("cr18=0x%x\n", cr18); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_debug.h b/drivers/gpu/drm/sisvga/sisvga_debug.h new file mode 100644 index 0000000000000..155f6db67e94a --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_debug.h @@ -0,0 +1,17 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#ifndef SISVGA_DEBUG_H +#define SISVGA_DEBUG_H + +struct sisvga_device; + +void sisvga_debug_print_mode(struct sisvga_device* sdev); +void sisvga_debug_print_regs(struct sisvga_device* sdev); + +#endif diff --git a/drivers/gpu/drm/sisvga/sisvga_device.c b/drivers/gpu/drm/sisvga/sisvga_device.c new file mode 100644 index 0000000000000..f78f81138cd3b --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_device.c @@ -0,0 +1,592 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> +#include <drm/drm_pci.h> +#include <linux/device.h> +#include <linux/ioport.h> +#include "sisvga_reg.h" + +static int modify_vga_status(struct sisvga_device *sdev, bool enable) +{ + static const u8 mask = 0x01; + u8 newvga, vga; + int i = 0; + + /* Tests and modifies the VGA enable bit. The loop waits + * several microseconds for success if necessary. */ + + vga = RREG8(REG_VGA); + + if (enable) + newvga = vga | 0x01; + else + newvga = vga & 0xfe; + + do { + if ((vga & mask) == (newvga & mask)) + return 0; + if (i) + udelay(10); + else + WREG8(REG_VGA, newvga); + vga = RREG8(REG_VGA); + } while (i++ < 50); + + return -ETIMEDOUT; +} + +static int enable_vga(struct sisvga_device *sdev) +{ + int ret = modify_vga_status(sdev, true); + if (ret < 0) + DRM_ERROR("sisvga: could not enable VGA card"); + return ret; +} + +static int disable_vga(struct sisvga_device *sdev) +{ + int ret = modify_vga_status(sdev, false); + if (ret < 0) + DRM_ERROR("sisvga: could not disable VGA card"); + return ret; +} + +static int modify_device_lock(struct sisvga_device *sdev, u8 wr, u8 rd) +{ + u8 sr05; + int i = 0; + + /* Tests and modifies the device's lock. The loop waits + * several microseconds for success if necessary. */ + + RREG_SR(0x05, sr05); + + do { + if (sr05 == rd) + return 0; + if (i) + udelay(10); + else + WREG_SR(0x05, wr); + RREG_SR(0x05, sr05); + } while (i++ < 50); + + return -ETIMEDOUT; +} + +static int unlock_device(struct sisvga_device *sdev) +{ + int ret = modify_device_lock(sdev, 0x86, 0xa1); + if (ret < 0) + DRM_ERROR("sisvga: could not unlock SiS extended registers"); + return ret; +} + +static int lock_device(struct sisvga_device *sdev) +{ + int ret = modify_device_lock(sdev, 0x00, 0x21); + if (ret < 0) + DRM_ERROR("sisvga: could not lock SiS extended registers"); + return ret; +} + +/* + * sisvga_device::regs + */ + +static int sisvga_regs_init(struct sisvga_ioports* regs, + struct pci_dev *pdev) +{ + resource_size_t base, size; + int err; + + base = pci_resource_start(pdev, SISVGA_PCI_BAR_REGS); + size = pci_resource_len(pdev, SISVGA_PCI_BAR_REGS); + + regs->res = request_region(base, size, "sisvga"); + if (!regs->res) { + DRM_ERROR("sisvga: can't reserve I/O ports\n"); + return -ENXIO; + } + + regs->mem = pci_iomap(pdev, SISVGA_PCI_BAR_REGS, 0); + if (!regs->mem) { + DRM_ERROR("sisvga: can't map I/O ports\n"); + err = -ENOMEM; + goto err; + } + + return 0; + +err: + release_resource(regs->res); + return err; +} + +static void sisvga_regs_fini(struct sisvga_ioports *regs, + struct pci_dev *pdev) +{ + pci_iounmap(pdev, regs->mem); + release_resource(regs->res); +} + +/* + * sisvga_device::mmio + */ + +static int sisvga_mmio_init(struct sisvga_devmem *mmio, + struct sisvga_device *sdev, + struct pci_dev *pdev) +{ + u8 sr0b; + resource_size_t base, size; + + RREG_SR(0x0b, sr0b); + + sr0b |= 0x60; /* map MMIO range */ + + WREG_SR(0x0b, sr0b); + + base = pci_resource_start(pdev, SISVGA_PCI_BAR_MMIO); + size = pci_resource_len(pdev, SISVGA_PCI_BAR_MMIO); + + mmio->base = base; + mmio->size = size; + mmio->mtrr = 0; + + mmio->res = request_mem_region(base, size, "sisvga-mmio"); + if (!mmio->res) { + DRM_ERROR("sisvga: can't reserve MMIO memory\n"); + return -ENXIO; + } + + mmio->mem = ioremap(base, size); + if (!mmio->mem) { + DRM_ERROR("sisvga: can't map MMIO memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void sisvga_mmio_fini(struct sisvga_devmem *mmio, + struct pci_dev *pdev) +{ + iounmap(mmio->mem); + release_region(mmio->base, mmio->size); +} + +/* + * sisvga_device::vram + */ + +static int sisvga_vram_init(struct sisvga_devmem *vram, + struct sisvga_device *sdev, + struct pci_dev *pdev) +{ + resource_size_t base, size; + u8 misc, gr06, sr06, sr0b, sr0c, sr20, sr21, sr2d; + unsigned long sizeexp; + + base = pci_resource_start(pdev, SISVGA_PCI_BAR_VRAM); + size = pci_resource_len(pdev, SISVGA_PCI_BAR_VRAM); + + misc = RREG8(REG_MISC_IN); + RREG_GR(0x06, gr06); + RREG_SR(0x06, sr06); + RREG_SR(0x0b, sr0b); + RREG_SR(0x0c, sr0c); + RREG_SR(0x20, sr20); + RREG_SR(0x21, sr21); + RREG_SR(0x2d, sr2d); + + misc |= 0x02 | /* allow CPU access to vram */ + 0x01; /* Color-graphics memory range enabled */ + gr06 &= 0xf3; /* select memory map 0 */ + sr06 |= 0x80; /* Linear addressing enabled */ + sr0b |= 0x20 | /* map video memory */ + 0x01; /* CPU-driven bitblt enabled */ + sr0c |= 0x80; /* Graphics-mode 32-bit memory access enabled */ + sr20 = (base & 0x07f80000) >> 19; /* Linear addressing base */ + /* We're mapping the VRAM size to a 3-bit field in sr21, such + * that + * + * size = 2 ^ sr21[7:5] * 512 * 1024 + * + * 512 KiB maps to 0x00, 1 MiB maps to 0x20, 2 maps to 0x04, + * and so on. The manual only specifies bits [6:5], but bit + * 7 works as well. This way, cards with more than 4 MiB of + * VRAM, such as the Diamond Speedstar A70, are supported. + * + * If the device fits the spec, (i.e., has at most 4 MiB of + * VRAM) we do the safe thing and preserve the reserved bit + * no 7. + */ + sizeexp = ((size >> 20) - 1); + if (size > KiB_TO_BYTE(4096)) { + sr21 = sizeexp << 5; + } else { + sr21 &= 0x80; /* preserve reserved bits */ + sr21 |= (sizeexp & 0x03) << 5; + } + sr21 |= (base & 0xf8000000) >> 27; /* Linear addressing base */ + sr2d &= 0xf0; /* preserve reserved bits */ + sr2d |= 0x00; /* TODO: page size depends on bus type! */ + + WREG8(REG_MISC_OUT, misc); + WREG_GR(0x06, gr06); + WREG_SR(0x06, sr06); + + //WREG_SR(0x0b, sr0b); + //WREG_SR(0x0c, sr0c); + WREG_SR(0x20, sr20); + WREG_SR(0x21, sr21); + //WREG_SR(0x2d, sr2d); + + arch_io_reserve_memtype_wc(base, size); + + vram->base = base; + vram->size = size; + vram->mtrr = arch_phys_wc_add(base, size); + + vram->res = request_mem_region(base, size, "sisvga-vram"); + if (!vram->res) { + DRM_ERROR("sisvga: can't reserve vram registers\n"); + return -ENXIO; + } + + vram->mem = ioremap_wc(base, size); + if (!vram->mem) { + DRM_ERROR("sisvga: can't map video ram\n"); + return -ENOMEM; + } + + return 0; +} + +static void sisvga_vram_fini(struct sisvga_devmem *vram, + struct pci_dev *pdev) +{ + arch_io_free_memtype_wc(vram->base, vram->size); + arch_phys_wc_del(vram->mtrr); + + iounmap(vram->mem); + release_region(vram->base, vram->size); +} + +/* + * Mode-config funcs + */ + +static struct drm_framebuffer* sisvga_mode_config_funcs_fb_create( + struct drm_device *dev, struct drm_file *filp, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *gem_obj; + struct sisvga_framebuffer *sis_fb; + int ret; + + gem_obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]); + if (!gem_obj) + return ERR_PTR(-ENOENT); + + sis_fb = sisvga_framebuffer_create(dev, gem_obj, mode_cmd); + if (IS_ERR(sis_fb)) { + ret = PTR_ERR(sis_fb); + goto err_sisvga_framebuffer_create; + } + + drm_gem_object_put_unlocked(gem_obj); + + return &sis_fb->base; + +err_sisvga_framebuffer_create: + drm_gem_object_put_unlocked(gem_obj); + return ERR_PTR(ret); +} + +static const struct drm_mode_config_funcs sisvga_mode_config_funcs = { + .fb_create = sisvga_mode_config_funcs_fb_create, +}; + +static int sisvga_mode_config_init(struct sisvga_device *sdev) +{ + static const uint32_t primary_formats[] = { + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB565, + DRM_FORMAT_C8 + }; + static const uint32_t cursor_formats[] = { + DRM_FORMAT_XRGB8888 + }; + static const uint64_t format_modifiers[] = { + DRM_FORMAT_MOD_INVALID + }; + + int ret; + struct sisvga_plane *sis_primary; + struct sisvga_plane *sis_cursor; + struct sisvga_crtc *sis_crtc; + struct sisvga_encoder *sis_encoder; + struct sisvga_connector *sis_connector; + + drm_mode_config_init(&sdev->base); + sdev->base.mode_config.max_width = sdev->info->max_htotal; + sdev->base.mode_config.max_height = sdev->info->max_vtotal; + sdev->base.mode_config.funcs = &sisvga_mode_config_funcs; + sdev->base.mode_config.fb_base = sdev->vram.base; + sdev->base.mode_config.preferred_depth = sdev->info->preferred_bpp; + + /* Planes */ + + sis_primary = sisvga_plane_create(&sdev->base, primary_formats, + ARRAY_SIZE(primary_formats), + format_modifiers, + DRM_PLANE_TYPE_PRIMARY); + if (IS_ERR(sis_primary)) { + ret = PTR_ERR(sis_primary); + goto err_sisvga_plane_create_primary; + } + + sis_cursor = sisvga_plane_create(&sdev->base, cursor_formats, + ARRAY_SIZE(cursor_formats), + format_modifiers, + DRM_PLANE_TYPE_CURSOR); + if (IS_ERR(sis_cursor)) { + ret = PTR_ERR(sis_cursor); + goto err_sisvga_plane_create_cursor; + } + + /* CRTCs */ + + sis_crtc = sisvga_crtc_create(&sdev->base, &sis_primary->base, + &sis_cursor->base); + if (IS_ERR(sis_crtc)) { + ret = PTR_ERR(sis_crtc); + goto err_sisvga_crtc_create; + } + + /* Encoders */ + + sis_encoder = sisvga_encoder_create(DRM_MODE_ENCODER_DAC, + &sdev->base); + if (IS_ERR(sis_encoder)) { + ret = PTR_ERR(sis_encoder); + goto err_sisvga_encoder_create; + } + + sis_encoder->base.possible_crtcs = (1ul << sis_crtc->base.index); + + /* Connectors */ + + sis_connector = sisvga_connector_create_vga(&sdev->base); + if (IS_ERR(sis_connector)) { + ret = PTR_ERR(sis_connector); + goto err_sisvga_connector_create_vga; + } + + ret = drm_mode_connector_attach_encoder(&sis_connector->base, + &sis_encoder->base); + if (ret < 0) + goto err_drm_mode_connector_attach_encoder; + + return 0; + +err_drm_mode_connector_attach_encoder: + /* fall through */ +err_sisvga_connector_create_vga: + /* fall through */ +err_sisvga_encoder_create: + /* fall through */ +err_sisvga_crtc_create: + /* fall through */ +err_sisvga_plane_create_cursor: + /* fall through */ +err_sisvga_plane_create_primary: + drm_mode_config_cleanup(&sdev->base); + return ret; +} + +/* + * struct sisvga_device + */ + +int sisvga_device_init(struct sisvga_device *sdev, + struct drm_driver *driver, struct pci_dev *pdev, + const struct sisvga_device_info *info) +{ + int ret; + + /* DRM initialization + */ + + ret = drm_dev_init(&sdev->base, driver, &pdev->dev); + if (ret) + return ret; + sdev->base.dev_private = sdev; + sdev->base.pdev = pdev; + + if (drm_core_check_feature(&sdev->base, DRIVER_USE_AGP)) { + if (pci_find_capability(sdev->base.pdev, PCI_CAP_ID_AGP)) + sdev->base.agp = drm_agp_init(&sdev->base); + if (sdev->base.agp) { + sdev->base.agp->agp_mtrr = arch_phys_wc_add( + sdev->base.agp->agp_info.aper_base, + sdev->base.agp->agp_info.aper_size * + 1024 * 1024); + } + } + + /* Make VGA and extended registers available. Later initialization + * requires registers, so this has to be done first. + */ + + sdev->info = info; + + ret = sisvga_regs_init(&sdev->regs, sdev->base.pdev); + if (ret < 0) + goto err_sisvga_regs_init; + + ret = enable_vga(sdev); + if (ret < 0) + goto err_enable_vga; + + ret = unlock_device(sdev); + if (ret < 0) + goto err_unlock_device; + + /* The MMIO region can contain VGA memory or command buffer. We + * always map the latter. + */ + + ret = sisvga_mmio_init(&sdev->mmio, sdev, sdev->base.pdev); + if (ret < 0) + goto err_sisvga_mmio_init; + + /* Next is memory management. We set-up the framebuffer memory and + * memory manager for the card. + */ + + ret = sisvga_vram_init(&sdev->vram, sdev, sdev->base.pdev); + if (ret < 0) + goto err_sisvga_vram_init; + + ret = sisvga_ttm_init(&sdev->ttm, &sdev->base, + sdev->vram.size >> PAGE_SHIFT); + if (ret < 0) + goto err_sisvga_ttm_init; + + /* One by one, we enable all stages of the display pipeline and + * connect them with each other. + */ + + ret = sisvga_mode_config_init(sdev); + if (ret < 0) + goto err_sisvga_mode_config_init; + + /* With the display pipeline running, we can now start fbdev + * emulation. This will also enable a framebuffer console, if + * configured. + */ + + ret = sisvga_fbdev_init(&sdev->fbdev, &sdev->base, + info->preferred_bpp); + if (ret < 0) + goto err_sisvga_fbdev_init; + + return 0; + +err_sisvga_fbdev_init: + drm_mode_config_cleanup(&sdev->base); +err_sisvga_mode_config_init: + sisvga_ttm_fini(&sdev->ttm); +err_sisvga_ttm_init: + sisvga_vram_fini(&sdev->vram, sdev->base.pdev); +err_sisvga_vram_init: + sisvga_mmio_fini(&sdev->mmio, sdev->base.pdev); +err_sisvga_mmio_init: + lock_device(sdev); +err_unlock_device: + disable_vga(sdev); +err_enable_vga: + sisvga_regs_fini(&sdev->regs, sdev->base.pdev); +err_sisvga_regs_init: + drm_dev_fini(&sdev->base); + return ret; +} + +void sisvga_device_fini(struct sisvga_device *sdev) +{ + struct drm_device *dev = &sdev->base; + + sisvga_fbdev_fini(&sdev->fbdev); + + drm_mode_config_cleanup(dev); + + sisvga_ttm_fini(&sdev->ttm); + sisvga_vram_fini(&sdev->vram, dev->pdev); + sisvga_mmio_fini(&sdev->mmio, dev->pdev); + + lock_device(sdev); + disable_vga(sdev); + + sisvga_regs_fini(&sdev->regs, dev->pdev); + + dev->dev_private = NULL; +} + +int sisvga_device_mmap(struct sisvga_device* sdev, struct file *filp, + struct vm_area_struct *vma) +{ + return ttm_bo_mmap(filp, vma, &sdev->ttm.bdev); +} + +int sisvga_device_create_dumb(struct sisvga_device *sdev, + struct drm_file *file, + struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = sisvga_gem_create(&sdev->base, args->size, false, &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_put_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int sisvga_device_mmap_dumb_offset(struct sisvga_device *sdev, + struct drm_file *file, uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + struct sisvga_bo *sis_bo; + + obj = drm_gem_object_lookup(file, handle); + if (obj == NULL) + return -ENOENT; + + sis_bo = gem_to_sisvga_bo(obj); + *offset = sisvga_bo_mmap_offset(sis_bo); + + drm_gem_object_put_unlocked(obj); + return 0; +} diff --git a/drivers/gpu/drm/sisvga/sisvga_device.h b/drivers/gpu/drm/sisvga/sisvga_device.h new file mode 100644 index 0000000000000..5e2b34bd652e7 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_device.h @@ -0,0 +1,268 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#ifndef SISVGA_DEVICE_H +#define SISVGA_DEVICE_H + +/* Set SISVGA_REGS to the standard location for + * memory-mapped I/O ports. */ +#define SISVGA_REGS \ + ((void __iomem *)((sdev)->regs.mem)) + +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_fb_helper.h> +#include <drm/drmP.h> // required by drm/drm_gem.h +#include <drm/drm_gem.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <linux/compiler.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "sisvga_drv.h" +#include "sisvga_modes.h" +#include "sisvga_vclk.h" + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) + +#define SISVGA_PCI_BAR_VRAM 0 /* BAR 0 contains VRAM */ +#define SISVGA_PCI_BAR_MMIO 1 /* BAR 1 contains MMIO region */ +#define SISVGA_PCI_BAR_REGS 2 /* BAR 2 contains VGA registers */ + +#define SISVGA_LUT_SIZE 256 + +struct drm_device; +struct drm_file; +struct drm_mode_fb_cmd2; +struct resource; + +enum sisvga_model { + SISVGA_MODEL_6202, + SISVGA_MODEL_6215, + SISVGA_MODEL_6326 +}; + +struct sisvga_device_info { + + enum sisvga_model model; + + /* Video RAM */ + resource_size_t max_size; /* Byte */ + + /* RAMDAC speed */ + int max_clock; /* KHz */ + + /* Bitmask of supported VCLK frequencies */ + unsigned long supported_vclks; + + /* CRTC */ + int max_htotal; + int max_hsync_start; + int max_hsync_end; + int max_hdisplay; + int max_vtotal; + int max_vsync_start; + int max_vsync_end; + int max_vdisplay; + int max_bpp; + int preferred_bpp; + + /* List of all supported VGA modes */ + const struct sisvga_mode* vga_modes; + size_t vga_modes_len; +}; + +#define SISVGA_DEVICE_INFO(model_, size_, clock_, supported_vclks_, htotal_,\ + hsync_start_, hsync_end_, hdisplay_, vtotal_,\ + vsync_start_, vsync_end_, vdisplay_,\ + bpp_, preferred_bpp_,\ + vga_modes_, vga_modes_len_)\ + .model = (model_),\ + .max_size = (size_),\ + .max_clock = (clock_),\ + .supported_vclks = (supported_vclks_),\ + .max_htotal = (htotal_),\ + .max_hsync_start = (hsync_start_),\ + .max_hsync_end = (hsync_end_),\ + .max_hdisplay = (hdisplay_),\ + .max_vtotal = (vtotal_),\ + .max_vsync_start = (vsync_start_),\ + .max_vsync_end = (vsync_end_),\ + .max_vdisplay = (vdisplay_),\ + .max_bpp = (bpp_),\ + .preferred_bpp = (preferred_bpp_),\ + .vga_modes = (vga_modes_),\ + .vga_modes_len = (vga_modes_len_) + +#define SISVGA_VCLK_BIT(vclk_) \ + (1ul << (unsigned long)(vclk_)) + +struct sisvga_ddc { + struct drm_device *dev; + u8 scl_mask; + u8 sda_mask; + + struct i2c_adapter adapter; + + int udelay; /* I2C delay, in usec */ + int timeout; /* I2C timeout, in jiffies */ + + /* saved register state */ + u8 sr01; +}; + +struct sisvga_connector { + struct drm_connector base; + struct sisvga_ddc ddc; +}; + +struct sisvga_plane { + struct drm_plane base; +}; + +struct sisvga_crtc { + struct drm_crtc base; + u8 lut[SISVGA_LUT_SIZE][3]; + size_t lut_len; + bool lut_24; +}; + +struct sisvga_encoder { + struct drm_encoder base; +}; + +struct sisvga_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *gem_obj; +}; + +struct sisvga_fbdev { + struct drm_fb_helper helper; + struct sisvga_framebuffer *fb; + + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; +}; + +struct sisvga_ioports { + struct resource *res; + void __iomem *mem; +}; + +struct sisvga_devmem { + resource_size_t base; + resource_size_t size; + int mtrr; + struct resource *res; + void __iomem *mem; +}; + +struct sisvga_ttm { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; +}; + +struct sisvga_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + + /* Supported placements are VRAM and SYSTEM */ + struct ttm_place placements[3]; + int pin_count; +}; +#define gem_to_sisvga_bo(gobj) container_of((gobj), struct sisvga_bo, gem) + +struct sisvga_device { + struct drm_device base; + + const struct sisvga_device_info *info; + + struct sisvga_ioports regs; + struct sisvga_devmem mmio; + struct sisvga_devmem vram; + + struct sisvga_ttm ttm; + + struct sisvga_fbdev fbdev; +}; + +#define MODEL_IS_EQ(num_) \ + ((sdev)->info->model == SISVGA_MODEL_ ## num_) + +#define MODEL_IS_LE(num_) \ + ((sdev)->info->model <= SISVGA_MODEL_ ## num_) + +#define MODEL_IS_GE(num_) \ + ((sdev)->info->model >= SISVGA_MODEL_ ## num_) + +#define MODEL_IS_LT(num_) \ + (!MODEL_IS_GE(num_)) + +#define MODEL_IS_GT(num_) \ + (!MODEL_IS_LE(num_)) + +int sisvga_device_init(struct sisvga_device *sdev, + struct drm_driver *driver, struct pci_dev *pdev, + const struct sisvga_device_info *info); +void sisvga_device_fini(struct sisvga_device *sdev); +int sisvga_device_mmap(struct sisvga_device *sdev, struct file *filp, + struct vm_area_struct *vma); +int sisvga_device_create_dumb(struct sisvga_device *sdev, + struct drm_file *file, + struct drm_mode_create_dumb *args); + +struct sisvga_connector* sisvga_connector_create_vga(struct drm_device *dev); + +struct sisvga_plane* sisvga_plane_create(struct drm_device *dev, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type); + +struct sisvga_crtc* sisvga_crtc_create(struct drm_device *dev, + struct drm_plane *primary_plane, + struct drm_plane *cursor_plane); + +int sisvga_ddc_init(struct sisvga_ddc *sis_ddc, struct drm_device *dev); +void sisvga_ddc_fini(struct sisvga_ddc *sis_ddc); + +struct sisvga_encoder* sisvga_encoder_create(int encoder_type, + struct drm_device *dev); + +int sisvga_fbdev_init(struct sisvga_fbdev *sis_fbdev, + struct drm_device *dev, int bpp); +void sisvga_fbdev_fini(struct sisvga_fbdev *sis_fbdev); + +struct sisvga_framebuffer* sisvga_framebuffer_create( + struct drm_device *dev, + struct drm_gem_object *gem_obj, + const struct drm_mode_fb_cmd2 *mode_cmd); + +struct sisvga_framebuffer* sisvga_framebuffer_of(struct drm_framebuffer *fb); + +int sisvga_ttm_init(struct sisvga_ttm *ttm, + struct drm_device *dev, + unsigned long p_size); +void sisvga_ttm_fini(struct sisvga_ttm *ttm); + +int sisvga_bo_create(struct drm_device *dev, int size, int align, + uint32_t flags, struct sisvga_bo **sis_bo_out); +void sisvga_bo_unref(struct sisvga_bo **sis_bo); +int sisvga_bo_reserve(struct sisvga_bo *bo, bool no_wait); +void sisvga_bo_unreserve(struct sisvga_bo *bo); +u64 sisvga_bo_mmap_offset(struct sisvga_bo *sis_bo); +int sisvga_bo_pin(struct sisvga_bo *bo, u32 pl_flag, u64 *gpu_addr); +int sisvga_bo_unpin(struct sisvga_bo *bo); +int sisvga_bo_push_to_system(struct sisvga_bo *bo); + +int sisvga_gem_create(struct drm_device *dev, u32 size, bool iskernel, + struct drm_gem_object **obj); + +#endif /* SISVGA_DEVICE_H */ diff --git a/drivers/gpu/drm/sisvga/sisvga_drv.c b/drivers/gpu/drm/sisvga/sisvga_drv.c new file mode 100644 index 0000000000000..c06a08f377d70 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_drv.c @@ -0,0 +1,505 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_drv.h" +#include <drm/drm_pciids.h> +#include <linux/console.h> +#include <linux/module.h> +#include "sisvga_device.h" + +static const struct sisvga_mode sisvga_6202_mode_list[] = { + /* VGA modes */ + { SISVGA_MODE("13", 320, 200, 70, 8, 25175, 0, 0) }, + /* Enhanced video modes */ + { SISVGA_MODE("2D", 640, 350, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E", 640, 480, 60, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E*", 640, 480, 72, 8, 31500, 0, 0) }, + { SISVGA_MODE("2E+", 640, 480, 75, 8, 31500, 0, 0) }, + { SISVGA_MODE("2F", 640, 400, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("30", 800, 600, 56, 8, 36000, 0, 0) }, + { SISVGA_MODE("30*", 800, 600, 60, 8, 40000, 0, 0) }, + { SISVGA_MODE("30+", 800, 600, 72, 8, 50000, 0, 0) }, + { SISVGA_MODE("30#", 800, 600, 75, 8, 50000, 0, 0) }, + { SISVGA_MODE("38i", 1024, 768, 87, 8, 44900, 0, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("38n", 1024, 768, 60, 8, 65000, 0, 0) }, + { SISVGA_MODE("38n+", 1024, 768, 70, 8, 75000, 0, 0) }, + { SISVGA_MODE("38n#", 1024, 768, 75, 8, 80000, 0, 0) }, + { SISVGA_MODE("3Ai", 1280, 1024, 89, 8, 80000, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("3An", 1280, 1024, 60, 8, 110000, 2048, 0) }, + { SISVGA_MODE("40", 320, 200, 70, 15, 25175, 0, 0) }, + { SISVGA_MODE("41", 320, 200, 70, 16, 25175, 0, 0) }, + { SISVGA_MODE("42", 320, 200, 70, 24, 25175, 0, 0) }, + { SISVGA_MODE("43", 640, 480, 60, 15, 25175, 0, 0) }, + { SISVGA_MODE("43*", 640, 480, 72, 15, 31500, 0, 0) }, + { SISVGA_MODE("43+", 640, 480, 75, 15, 31500, 0, 0) }, + { SISVGA_MODE("44", 640, 480, 60, 16, 25175, 0, 0) }, + { SISVGA_MODE("44*", 640, 480, 72, 16, 31500, 0, 0) }, + { SISVGA_MODE("44+", 640, 480, 75, 16, 31500, 0, 0) }, + { SISVGA_MODE("45", 640, 480, 60, 24, 25175, 0, 0) }, + { SISVGA_MODE("45*", 640, 480, 72, 24, 31500, 0, 0) }, + { SISVGA_MODE("45+", 640, 480, 75, 24, 31500, 0, 0) }, + { SISVGA_MODE("46", 800, 600, 56, 15, 36000, 0, 0) }, + { SISVGA_MODE("46*", 800, 600, 60, 15, 40000, 0, 0) }, + { SISVGA_MODE("46+", 800, 600, 72, 15, 50000, 0, 0) }, + { SISVGA_MODE("46#", 800, 600, 75, 15, 50000, 0, 0) }, + { SISVGA_MODE("47", 800, 600, 56, 16, 36000, 0, 0) }, + { SISVGA_MODE("47*", 800, 600, 60, 16, 40000, 0, 0) }, + { SISVGA_MODE("47+", 800, 600, 72, 16, 50000, 0, 0) }, + { SISVGA_MODE("47#", 800, 600, 75, 16, 50000, 0, 0) }, + { SISVGA_MODE("48", 800, 600, 56, 24, 36000, 2048, 0) }, + { SISVGA_MODE("48*", 800, 600, 60, 24, 40000, 2048, 0) }, + { SISVGA_MODE("48+", 800, 600, 72, 24, 50000, 2048, 0) }, + { SISVGA_MODE("48#", 800, 600, 75, 24, 50000, 2048, 0) }, + { SISVGA_MODE("49i", 1024, 768, 87, 15, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("49n", 1024, 768, 60, 15, 65000, 2048, 0) }, + { SISVGA_MODE("49n+", 1024, 768, 70, 15, 75000, 2048, 0) }, + { SISVGA_MODE("49n#", 1024, 768, 75, 15, 80000, 2048, 0) }, + { SISVGA_MODE("4Ai", 1024, 768, 87, 16, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4An", 1024, 768, 60, 16, 65000, 2048, 0) }, + { SISVGA_MODE("4An+", 1024, 768, 70, 16, 75000, 2048, 0) }, + { SISVGA_MODE("4An#", 1024, 768, 75, 16, 80000, 2048, 0) }, + { SISVGA_MODE("4Bi", 1024, 768, 87, 24, 44900, 4096, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4Ci", 1280, 1024, 89, 15, 80000, 4096, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4Di", 1280, 1024, 89, 16, 80000, 4096, + DRM_MODE_FLAG_INTERLACE) }, +}; + +static const struct sisvga_mode sisvga_6215_mode_list[] = { + /* VGA modes */ + { SISVGA_MODE("13", 320, 200, 70, 8, 25175, 0, 0) }, + /* Enhanced video modes */ + { SISVGA_MODE("2D", 640, 350, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E", 640, 480, 60, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E*", 640, 480, 72, 8, 31500, 0, 0) }, + { SISVGA_MODE("2E+", 640, 480, 75, 8, 31500, 0, 0) }, + { SISVGA_MODE("2E++", 640, 480, 85, 8, 36000, 0, 0) }, + { SISVGA_MODE("2F", 640, 400, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("30", 800, 600, 56, 8, 36000, 0, 0) }, + { SISVGA_MODE("30*", 800, 600, 60, 8, 40000, 0, 0) }, + { SISVGA_MODE("30+", 800, 600, 72, 8, 50000, 0, 0) }, + { SISVGA_MODE("30#", 800, 600, 75, 8, 50000, 0, 0) }, + { SISVGA_MODE("30##", 800, 600, 85, 8, 56300, 0, 0) }, + { SISVGA_MODE("38i", 1024, 768, 87, 8, 44900, 0, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("38n", 1024, 768, 60, 8, 65000, 0, 0) }, + { SISVGA_MODE("38n+", 1024, 768, 70, 8, 75000, 0, 0) }, + { SISVGA_MODE("38n#", 1024, 768, 75, 8, 80000, 0, 0) }, + { SISVGA_MODE("38n##", 1024, 768, 85, 8, 94500, 0, 0) }, + { SISVGA_MODE("3Ai", 1280, 1024, 87, 8, 80000, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("3An", 1280, 1024, 60, 8, 110000, 2048, 0) }, + { SISVGA_MODE("3An+", 1280, 1024, 75, 8, 135000, 2048, 0) }, + { SISVGA_MODE("40", 320, 200, 70, 15, 25175, 0, 0) }, + { SISVGA_MODE("41", 320, 200, 70, 16, 25175, 0, 0) }, + { SISVGA_MODE("42", 320, 200, 70, 24, 25175, 0, 0) }, + { SISVGA_MODE("43", 640, 480, 60, 15, 25175, 0, 0) }, + { SISVGA_MODE("43*", 640, 480, 72, 15, 31500, 0, 0) }, + { SISVGA_MODE("43+", 640, 480, 75, 15, 31500, 0, 0) }, + { SISVGA_MODE("43++", 640, 480, 85, 15, 36000, 0, 0) }, + { SISVGA_MODE("44", 640, 480, 60, 16, 25175, 0, 0) }, + { SISVGA_MODE("44*", 640, 480, 72, 16, 31500, 0, 0) }, + { SISVGA_MODE("44+", 640, 480, 75, 16, 31500, 0, 0) }, + { SISVGA_MODE("44++", 640, 480, 85, 16, 36000, 0, 0) }, + { SISVGA_MODE("45", 640, 480, 60, 24, 25175, 0, 0) }, + { SISVGA_MODE("45*", 640, 480, 72, 24, 31500, 2048, 0) }, + { SISVGA_MODE("45+", 640, 480, 75, 24, 31500, 2048, 0) }, + { SISVGA_MODE("45++", 640, 480, 85, 24, 36000, 0, 0) }, + { SISVGA_MODE("46", 800, 600, 56, 15, 36000, 0, 0) }, + { SISVGA_MODE("46*", 800, 600, 60, 15, 40000, 0, 0) }, + { SISVGA_MODE("46+", 800, 600, 72, 15, 50000, 2048, 0) }, + { SISVGA_MODE("46#", 800, 600, 75, 15, 50000, 2048, 0) }, + { SISVGA_MODE("46##", 800, 600, 85, 15, 56300, 0, 0) }, + { SISVGA_MODE("47", 800, 600, 56, 16, 36000, 0, 0) }, + { SISVGA_MODE("47*", 800, 600, 60, 16, 40000, 0, 0) }, + { SISVGA_MODE("47+", 800, 600, 72, 16, 50000, 2048, 0) }, + { SISVGA_MODE("47#", 800, 600, 75, 16, 50000, 2048, 0) }, + { SISVGA_MODE("47##", 800, 600, 85, 16, 56300, 0, 0) }, + { SISVGA_MODE("48", 800, 600, 56, 24, 36000, 2048, 0) }, + { SISVGA_MODE("48*", 800, 600, 60, 24, 40000, 2048, 0) }, + { SISVGA_MODE("48+", 800, 600, 72, 24, 50000, 2048, 0) }, + { SISVGA_MODE("48#", 800, 600, 75, 24, 50000, 2048, 0) }, + { SISVGA_MODE("48##", 800, 600, 85, 24, 56300, 2048, 0) }, + { SISVGA_MODE("49i", 1024, 768, 87, 15, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("49n", 1024, 768, 60, 15, 65000, 2048, 0) }, + { SISVGA_MODE("49n+", 1024, 768, 70, 15, 75000, 2048, 0) }, + { SISVGA_MODE("49n#", 1024, 768, 75, 15, 80000, 2048, 0) }, + { SISVGA_MODE("49n##", 1024, 768, 85, 15, 94500, 2048, 0) }, + { SISVGA_MODE("4Ai", 1024, 768, 87, 16, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4An", 1024, 768, 60, 16, 65000, 2048, 0) }, + { SISVGA_MODE("4An+", 1024, 768, 70, 16, 75000, 2048, 0) }, + { SISVGA_MODE("4An#", 1024, 768, 75, 16, 80000, 2048, 0) }, + { SISVGA_MODE("4An##", 1024, 768, 85, 16, 94500, 2048, 0) }, +}; + +static const struct sisvga_mode sisvga_6326_mode_list[] = { + /* VGA modes */ + { SISVGA_MODE("13", 320, 200, 70, 8, 25175, 0, 0) }, + /* Enhanced video modes */ + { SISVGA_MODE("2D", 640, 350, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E", 640, 480, 60, 8, 25175, 0, 0) }, + { SISVGA_MODE("2E*", 640, 480, 72, 8, 31500, 0, 0) }, + { SISVGA_MODE("2E+", 640, 480, 75, 8, 31500, 0, 0) }, + { SISVGA_MODE("2E++", 640, 480, 85, 8, 36000, 0, 0) }, + { SISVGA_MODE("2F", 640, 400, 70, 8, 25175, 0, 0) }, + { SISVGA_MODE("30", 800, 600, 56, 8, 36000, 0, 0) }, + { SISVGA_MODE("30*", 800, 600, 60, 8, 40000, 0, 0) }, + { SISVGA_MODE("30+", 800, 600, 72, 8, 50000, 0, 0) }, + { SISVGA_MODE("30#", 800, 600, 75, 8, 50000, 0, 0) }, + { SISVGA_MODE("30##", 800, 600, 85, 8, 56300, 0, 0) }, + { SISVGA_MODE("38i", 1024, 768, 87, 8, 44900, 0, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("38n", 1024, 768, 60, 8, 65000, 0, 0) }, + { SISVGA_MODE("38n+", 1024, 768, 70, 8, 75000, 0, 0) }, + { SISVGA_MODE("38n#", 1024, 768, 75, 8, 80000, 0, 0) }, + { SISVGA_MODE("38n##", 1024, 768, 85, 8, 94500, 0, 0) }, + { SISVGA_MODE("3Ai", 1280, 1024, 87, 8, 80000, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("3An", 1280, 1024, 60, 8, 110000, 2048, 0) }, + { SISVGA_MODE("3An+", 1280, 1024, 75, 8, 135000, 2048, 0) }, + { SISVGA_MODE("3Ci", 1600, 1200, 87, 8, 135000, 2048, 0) }, + { SISVGA_MODE("3C", 1600, 1200, 60, 8, 162000, 2048, 0) }, + { SISVGA_MODE("3C*", 1600, 1200, 65, 8, 175500, 2048, 0) }, + { SISVGA_MODE("40", 320, 200, 70, 15, 25175, 0, 0) }, + { SISVGA_MODE("41", 320, 200, 70, 16, 25175, 0, 0) }, + { SISVGA_MODE("42", 320, 200, 70, 24, 25175, 0, 0) }, + { SISVGA_MODE("43", 640, 480, 60, 15, 25175, 0, 0) }, + { SISVGA_MODE("43*", 640, 480, 72, 15, 31500, 0, 0) }, + { SISVGA_MODE("43+", 640, 480, 75, 15, 31500, 0, 0) }, + { SISVGA_MODE("43++", 640, 480, 85, 15, 36000, 0, 0) }, + { SISVGA_MODE("44", 640, 480, 60, 16, 25175, 0, 0) }, + { SISVGA_MODE("44*", 640, 480, 72, 16, 31500, 0, 0) }, + { SISVGA_MODE("44+", 640, 480, 75, 16, 31500, 0, 0) }, + { SISVGA_MODE("44++", 640, 480, 85, 16, 36000, 0, 0) }, + { SISVGA_MODE("45", 640, 480, 60, 24, 25175, 0, 0) }, + { SISVGA_MODE("45*", 640, 480, 72, 24, 31500, 2048, 0) }, + { SISVGA_MODE("45+", 640, 480, 75, 24, 31500, 2048, 0) }, + { SISVGA_MODE("45++", 640, 480, 85, 24, 36000, 0, 0) }, + { SISVGA_MODE("46", 800, 600, 56, 15, 36000, 0, 0) }, + { SISVGA_MODE("46*", 800, 600, 60, 15, 40000, 0, 0) }, + { SISVGA_MODE("46+", 800, 600, 72, 15, 50000, 2048, 0) }, + { SISVGA_MODE("46#", 800, 600, 75, 15, 50000, 2048, 0) }, + { SISVGA_MODE("46##", 800, 600, 85, 15, 56300, 0, 0) }, + { SISVGA_MODE("47", 800, 600, 56, 16, 36000, 0, 0) }, + { SISVGA_MODE("47*", 800, 600, 60, 16, 40000, 0, 0) }, + { SISVGA_MODE("47+", 800, 600, 72, 16, 50000, 2048, 0) }, + { SISVGA_MODE("47#", 800, 600, 75, 16, 50000, 2048, 0) }, + { SISVGA_MODE("47##", 800, 600, 85, 16, 56300, 0, 0) }, + { SISVGA_MODE("48", 800, 600, 56, 24, 36000, 2048, 0) }, + { SISVGA_MODE("48*", 800, 600, 60, 24, 40000, 2048, 0) }, + { SISVGA_MODE("48+", 800, 600, 72, 24, 50000, 2048, 0) }, + { SISVGA_MODE("48#", 800, 600, 75, 24, 50000, 2048, 0) }, + { SISVGA_MODE("48##", 800, 600, 85, 24, 56300, 2048, 0) }, + { SISVGA_MODE("49i", 1024, 768, 87, 15, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("49n", 1024, 768, 60, 15, 65000, 2048, 0) }, + { SISVGA_MODE("49n+", 1024, 768, 70, 15, 75000, 2048, 0) }, + { SISVGA_MODE("49n#", 1024, 768, 75, 15, 80000, 2048, 0) }, + { SISVGA_MODE("49n##", 1024, 768, 85, 15, 94500, 2048, 0) }, + { SISVGA_MODE("4Ai", 1024, 768, 87, 16, 44900, 2048, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4An", 1024, 768, 60, 16, 65000, 2048, 0) }, + { SISVGA_MODE("4An+", 1024, 768, 70, 16, 75000, 2048, 0) }, + { SISVGA_MODE("4An#", 1024, 768, 75, 16, 80000, 2048, 0) }, + { SISVGA_MODE("4An##", 1024, 768, 85, 16, 94500, 2048, 0) }, + { SISVGA_MODE("4Bi", 1024, 768, 87, 24, 44900, 4096, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4Bn", 1024, 768, 60, 24, 65000, 4096, 0) }, + { SISVGA_MODE("4Bn+", 1024, 768, 70, 24, 75000, 4096, 0) }, + { SISVGA_MODE("4Bn#", 1024, 768, 75, 24, 80000, 4096, 0) }, + { SISVGA_MODE("4Bn##", 1024, 768, 85, 24, 94500, 4096, 0) }, + { SISVGA_MODE("4Ci", 1280, 1024, 89, 15, 80000, 4096, + DRM_MODE_FLAG_INTERLACE) }, + { SISVGA_MODE("4Ci", 1280, 1024, 89, 16, 80000, 4096, + DRM_MODE_FLAG_INTERLACE) }, +}; + +static const struct sisvga_device_info sisvga_device_info_list[] = { + [SISVGA_MODEL_6202] = { + SISVGA_DEVICE_INFO(SISVGA_MODEL_6202, 2 * 1024 * 1024, 130000, + SISVGA_VCLK_BIT(SISVGA_VCLK_25175) | + SISVGA_VCLK_BIT(SISVGA_VCLK_28322) | + SISVGA_VCLK_BIT(SISVGA_VCLK_31500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_36000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_40000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_44889) | + SISVGA_VCLK_BIT(SISVGA_VCLK_50000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_65000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_75000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_77000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_80000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_94500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_110000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_120000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_130000) | + /* Only in modes list; not in VCKL spec */ + SISVGA_VCLK_BIT(SISVGA_VCLK_30000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_44900), + 2048, 2048, 2048, 1280, + 1024, 1024, 1024, 1024, + 24, 24, + sisvga_6202_mode_list, + ARRAY_SIZE(sisvga_6202_mode_list)) }, + [SISVGA_MODEL_6215] = { + SISVGA_DEVICE_INFO(SISVGA_MODEL_6215, 2 * 1024 * 1024, 135000, + SISVGA_VCLK_BIT(SISVGA_VCLK_25175) | + SISVGA_VCLK_BIT(SISVGA_VCLK_28322) | + SISVGA_VCLK_BIT(SISVGA_VCLK_31500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_36000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_40000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_44889) | + SISVGA_VCLK_BIT(SISVGA_VCLK_50000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_65000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_75000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_77000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_80000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_94500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_110000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_120000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_135000) | + /* Only in modes list; not in VCKL spec */ + SISVGA_VCLK_BIT(SISVGA_VCLK_30000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_44900), + 2048, 2048, 2048, 1280, + 1024, 1024, 1024, 1024, + 24, 24, + sisvga_6215_mode_list, + ARRAY_SIZE(sisvga_6215_mode_list)) }, + [SISVGA_MODEL_6326] = { + SISVGA_DEVICE_INFO(SISVGA_MODEL_6326, 8 * 1024 * 1024, 175500, + SISVGA_VCLK_BIT(SISVGA_VCLK_25175) | + SISVGA_VCLK_BIT(SISVGA_VCLK_28322) | + SISVGA_VCLK_BIT(SISVGA_VCLK_30000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_31500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_36000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_40000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_44889) | + SISVGA_VCLK_BIT(SISVGA_VCLK_50000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_65000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_77000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_80000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_94500) | + SISVGA_VCLK_BIT(SISVGA_VCLK_110000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_110000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_120000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_135000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_162000) | + SISVGA_VCLK_BIT(SISVGA_VCLK_175500) | + /* Only in modes list; not in VCKL spec */ + SISVGA_VCLK_BIT(SISVGA_VCLK_44900) | + SISVGA_VCLK_BIT(SISVGA_VCLK_56300) | + SISVGA_VCLK_BIT(SISVGA_VCLK_75000), + 4096, 4096, 4096, 1600, + 2048, 2048, 2048, 1200, + 24, 24, + sisvga_6326_mode_list, + ARRAY_SIZE(sisvga_6326_mode_list)) }, +}; + +/* + * DRM entry points + */ + +static void sisvga_driver_unload(struct drm_device *dev) +{ + struct sisvga_device *sdev = dev->dev_private; + + if (!sdev) + return; + + sisvga_device_fini(sdev); +} + +int sisvga_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct sisvga_device *sdev = file_priv->minor->dev->dev_private; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return -EINVAL; + + return sisvga_device_mmap(sdev, filp, vma); +} + +static const struct file_operations sisvga_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = sisvga_mmap, + .poll = drm_poll, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .read = drm_read, +}; + +static void sisvga_driver_gem_free_object(struct drm_gem_object *obj) +{ + struct sisvga_bo *sis_bo = gem_to_sisvga_bo(obj); + sisvga_bo_unref(&sis_bo); +} + +static int sisvga_driver_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct sisvga_device *sdev = dev->dev_private; + return sisvga_device_create_dumb(sdev, file_priv, args); +} + +static int sisvga_driver_dumb_mmap_offset(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *obj; + struct sisvga_bo *sis_bo; + + obj = drm_gem_object_lookup(file_priv, handle); + if (obj == NULL) + return -ENOENT; + + sis_bo = gem_to_sisvga_bo(obj); + *offset = sisvga_bo_mmap_offset(sis_bo); + + drm_gem_object_put_unlocked(obj); + return 0; +} + +static struct drm_driver sisvga_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET, + .unload = sisvga_driver_unload, + .fops = &sisvga_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + /* GEM interfaces */ + .gem_free_object = sisvga_driver_gem_free_object, + /* Dumb interfaces */ + .dumb_create = sisvga_driver_dumb_create, + .dumb_map_offset = sisvga_driver_dumb_mmap_offset, +}; + +/* + * PCI driver entry points + */ + +static const struct pci_device_id pciidlist[] = { + { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_6202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SISVGA_MODEL_6202 }, + { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_6215, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SISVGA_MODEL_6215 }, + { PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_6326, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SISVGA_MODEL_6326 }, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, pciidlist); + +static void sisvga_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return; + + ap->ranges[0].base = pci_resource_start(pdev, SISVGA_PCI_BAR_VRAM); + ap->ranges[0].size = pci_resource_len(pdev, SISVGA_PCI_BAR_VRAM); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + drm_fb_helper_remove_conflicting_framebuffers(ap, "sisfb", primary); + kfree(ap); +} + +static int sisvga_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + enum sisvga_model model; + struct sisvga_device* sdev; + + sisvga_kick_out_firmware_fb(pdev); + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + model = ent->driver_data & 0xf; + if (model >= ARRAY_SIZE(sisvga_device_info_list)) { + /* BUG: There should be a device info for every model. */ + DRM_ERROR("sisvga: unknown device model %d\n", model); + return -EINVAL; + } + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + ret = sisvga_device_init(sdev, &sisvga_drm_driver, pdev, + sisvga_device_info_list + model); + if (ret) + return ret; + + ret = drm_dev_register(&sdev->base, 0); + if (ret) + goto err_drm_dev_register; + + pci_set_drvdata(pdev, &sdev->base); + + return 0; + +err_drm_dev_register: + sisvga_device_fini(sdev); + return ret; +} + +static void sisvga_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static struct pci_driver sisvga_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = sisvga_probe, + .remove = sisvga_remove, +}; + +static int __init sisvga_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force()) + return -EINVAL; +#endif + + return pci_register_driver(&sisvga_pci_driver); +} + +static void __exit sisvga_exit(void) +{ + pci_unregister_driver(&sisvga_pci_driver); +} + +module_init(sisvga_init); +module_exit(sisvga_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sisvga/sisvga_drv.h b/drivers/gpu/drm/sisvga/sisvga_drv.h new file mode 100644 index 0000000000000..96e265396fc0c --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_drv.h @@ -0,0 +1,24 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#ifndef SISVGA_DRV_H +#define SISVGA_DRV_H + +#define DRIVER_AUTHOR "Thomas Zimmermann" +#define DRIVER_NAME "sisvga" +#define DRIVER_DESC "SiS graphics driver" +#define DRIVER_DATE "20160301" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define KiB_TO_BYTE(_kib) ((_kib) * 1024) +#define MiB_TO_KiB(_mib) ((_mib) * 1024) +#define MiB_TO_BYTE(_mib) KiB_TO_MiB((MiB_TO_KiB(_mib))) + +#endif diff --git a/drivers/gpu/drm/sisvga/sisvga_encoder.c b/drivers/gpu/drm/sisvga/sisvga_encoder.c new file mode 100644 index 0000000000000..297dd350bed2a --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_encoder.c @@ -0,0 +1,213 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> +#include <drm/drm_modeset_helper_vtables.h> +#include "sisvga_debug.h" +#include "sisvga_reg.h" + +static struct sisvga_encoder* sisvga_encoder_of(struct drm_encoder *encoder) +{ + return container_of(encoder, struct sisvga_encoder, base); +} + +/* + * DPMS helpers + */ + +static void set_encoder_dpms_mode(struct sisvga_device *sdev, int mode) +{ + u8 sr11, cr17; + + RREG_SR(0x11, sr11); + RREG_CR(0x17, cr17); + + switch (mode) { + case DRM_MODE_DPMS_ON: + cr17 |= 0x80; /* sync pulses enabled */ + sr11 &= 0x3f; /* clear power-management mode */ + break; + case DRM_MODE_DPMS_STANDBY: + cr17 |= 0x80; /* sync pulses enabled */ + sr11 &= 0x3f; /* clear power-management mode and...*/ + sr11 |= 0x40; /* ...force suspend mode */ + break; + case DRM_MODE_DPMS_SUSPEND: + cr17 |= 0x80; /* sync pulses enabled */ + sr11 &= 0x3f; /* clear power-management mode and...*/ + sr11 |= 0x80; /* ...force stand-by mode */ + break; + case DRM_MODE_DPMS_OFF: + cr17 &= 0x7f; /* sync pulses disabled */ + sr11 |= 0xc0; /* force off */ + break; + default: + DRM_ERROR("sisvga: invalid DPMS mode %d\n", mode); + return; + } + + WREG_CR(0x17, cr17); + WREG_SR(0x11, sr11); +} + +/* + * Encoder helper funcs + */ + +static void sisvga_encoder_helper_dpms(struct drm_encoder *encoder, int mode) +{ + set_encoder_dpms_mode(encoder->dev->dev_private, mode); +} + +static bool sisvga_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + long ret; + enum sisvga_vclk vclk; + enum sisvga_freq freq; + unsigned long num, denum, div, postscal, f; + struct sisvga_device *sdev = encoder->dev->dev_private; + const struct sisvga_device_info *info = sdev->info; + + if (mode->clock > info->max_clock) + return false; /* not enough bandwidth */ + + ret = sisvga_vclk_of_clock(adj_mode->clock, &vclk); + if (ret < 0) { + /* BUG: We should have detected this in mode_valid(). */ + DRM_INFO("sisvga: unsupported dot clock of %d KHz, error %ld", + mode->clock, -ret); + return false; + } + sisvga_vclk_regs(vclk, &freq, &num, &denum, &div, &postscal, &f); + + if (freq == SISVGA_FREQ_14318) { + + /* For modes that use the internal clock generator, we + * fixup the display size to better match the requested dot + * clock. */ + + long f_diff; + int dots; + + if ((mode->htotal % 9) == 0) + dots = 9; + else + dots = 8; + + f_diff = KHZ_TO_HZ(adj_mode->crtc_clock) - f; + } + + return true; +} + +static void sisvga_encoder_helper_prepare(struct drm_encoder *encoder) +{ + /* We disable the screen to allow for flicker-free + * mode switching. */ + set_encoder_dpms_mode(encoder->dev->dev_private, DRM_MODE_DPMS_OFF); + + sisvga_debug_print_regs(encoder->dev->dev_private); + sisvga_debug_print_mode(encoder->dev->dev_private); +} + +static void sisvga_encoder_helper_commit(struct drm_encoder *encoder) +{ + set_encoder_dpms_mode(encoder->dev->dev_private, DRM_MODE_DPMS_ON); + + sisvga_debug_print_regs(encoder->dev->dev_private); + sisvga_debug_print_mode(encoder->dev->dev_private); +} + +static void sisvga_encoder_helper_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ +} + +static void sisvga_encoder_helper_disable(struct drm_encoder *encoder) +{ + set_encoder_dpms_mode(encoder->dev->dev_private, DRM_MODE_DPMS_OFF); +} + +static void sisvga_encoder_helper_enable(struct drm_encoder *encoder) +{ + set_encoder_dpms_mode(encoder->dev->dev_private, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs sisvga_encoder_helper_funcs = { + .dpms = sisvga_encoder_helper_dpms, + .mode_fixup = sisvga_encoder_mode_fixup, + .prepare = sisvga_encoder_helper_prepare, + .commit = sisvga_encoder_helper_commit, + .mode_set = sisvga_encoder_helper_mode_set, + .disable = sisvga_encoder_helper_disable, + .enable = sisvga_encoder_helper_enable, +}; + +/* + * Encoder funcs + */ + +static void sisvga_encoder_destroy(struct drm_encoder *encoder) +{ + struct sisvga_encoder *sis_encoder = sisvga_encoder_of(encoder); + struct drm_device *dev = encoder->dev; + + drm_encoder_cleanup(&sis_encoder->base); + devm_kfree(dev->dev, sis_encoder); +} + +static const struct drm_encoder_funcs sisvga_encoder_funcs = { + .destroy = sisvga_encoder_destroy, +}; + +/* + * struct sis_encoder + */ + +static int sisvga_encoder_init(struct sisvga_encoder *sis_encoder, + int encoder_type, + struct drm_device *dev) +{ + int ret; + struct drm_encoder *encoder = &sis_encoder->base; + + ret = drm_encoder_init(dev, encoder, &sisvga_encoder_funcs, + encoder_type, NULL); + if (ret < 0) + return ret; + + drm_encoder_helper_add(encoder, &sisvga_encoder_helper_funcs); + + return 0; +} + +struct sisvga_encoder* sisvga_encoder_create(int encoder_type, + struct drm_device *dev) +{ + struct sisvga_encoder *sis_encoder; + int ret; + + sis_encoder = devm_kzalloc(dev->dev, sizeof(*sis_encoder), + GFP_KERNEL); + if (!sis_encoder) + return ERR_PTR(-ENOMEM); + + ret = sisvga_encoder_init(sis_encoder, DRM_MODE_ENCODER_DAC, dev); + if (ret) + goto err_sisvga_encoder_init; + + return sis_encoder; + +err_sisvga_encoder_init: + devm_kfree(dev->dev, sis_encoder); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_fbdev.c b/drivers/gpu/drm/sisvga/sisvga_fbdev.c new file mode 100644 index 0000000000000..47acae7a47779 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_fbdev.c @@ -0,0 +1,288 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drm_crtc_helper.h> +#include <drm/drmP.h> + +/* + * FB ops + */ + +static void memcpy_to(void* dst, const void* src, size_t size, bool is_iomem) +{ + if (is_iomem) + memcpy_toio(dst, src, size); + else + memcpy(dst, src, size); +} + +static void sisvga_fbdev_dirty_update(struct sisvga_fbdev *sis_fbdev, + int x, int y, int width, int height) +{ + int i; + struct sisvga_bo *bo; + int src_offset, dst_offset; + int bpp = sis_fbdev->fb->base.format->cpp[0]; + int ret = -EBUSY; + bool store_for_later = false; + int x2, y2; + unsigned long flags; + const u8 *src; + bool is_iomem; + u8 *dst; + + bo = gem_to_sisvga_bo(sis_fbdev->fb->gem_obj); + + /* + * try and reserve the BO, if we fail with busy + * then the BO is being moved and we should + * store up the damage until later. + */ + if (drm_can_sleep()) + ret = sisvga_bo_reserve(bo, true); + if (ret) { + if (ret != -EBUSY) + return; + + store_for_later = true; + } + + x2 = x + width - 1; + y2 = y + height - 1; + spin_lock_irqsave(&sis_fbdev->dirty_lock, flags); + + if (sis_fbdev->y1 < y) + y = sis_fbdev->y1; + if (sis_fbdev->y2 > y2) + y2 = sis_fbdev->y2; + if (sis_fbdev->x1 < x) + x = sis_fbdev->x1; + if (sis_fbdev->x2 > x2) + x2 = sis_fbdev->x2; + + if (store_for_later) { + sis_fbdev->x1 = x; + sis_fbdev->x2 = x2; + sis_fbdev->y1 = y; + sis_fbdev->y2 = y2; + spin_unlock_irqrestore(&sis_fbdev->dirty_lock, flags); + return; + } + + sis_fbdev->x1 = sis_fbdev->y1 = INT_MAX; + sis_fbdev->x2 = sis_fbdev->y2 = 0; + spin_unlock_irqrestore(&sis_fbdev->dirty_lock, flags); + + if (!bo->kmap.virtual) { + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) { + DRM_ERROR("failed to kmap fb updates\n"); + sisvga_bo_unreserve(bo); + return; + } + } + + dst = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); + src = sis_fbdev->helper.fbdev->screen_base; + + for (i = y; i <= y2; i++) { + /* assume equal stride for now */ + src_offset = i * sis_fbdev->fb->base.pitches[0] + (x * bpp); + dst_offset = src_offset; + memcpy_to(dst + dst_offset, src + src_offset, + (x2 - x + 1) * bpp, is_iomem); + } + + sisvga_bo_unreserve(bo); +} + +static void sisvga_fb_ops_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct sisvga_fbdev *sis_fbdev = info->par; + + drm_fb_helper_sys_fillrect(info, rect); + + sisvga_fbdev_dirty_update(sis_fbdev, rect->dx, rect->dy, rect->width, + rect->height); +} + +static void sisvga_fb_ops_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct sisvga_fbdev *sis_fbdev = info->par; + + drm_fb_helper_sys_copyarea(info, area); + + sisvga_fbdev_dirty_update(sis_fbdev, area->dx, area->dy, area->width, + area->height); +} + +static void sisvga_fb_ops_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct sisvga_fbdev *sis_fbdev = info->par; + + drm_fb_helper_sys_imageblit(info, image); + + sisvga_fbdev_dirty_update(sis_fbdev, image->dx, image->dy, image->width, + image->height); +} + +static struct fb_ops sisvga_fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = sisvga_fb_ops_fillrect, + .fb_copyarea = sisvga_fb_ops_copyarea, + .fb_imageblit = sisvga_fb_ops_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +/* + * Fbdev helpers + */ + +static int sisvga_fb_helper_fb_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_mode_fb_cmd2 mode_cmd; + void *sysram; + u32 size; + int ret; + struct drm_gem_object *gem_obj; + struct sisvga_framebuffer *sis_fb; + struct fb_info *info; + struct sisvga_fbdev *sis_fbdev = + container_of(helper, struct sisvga_fbdev, helper); + struct drm_device *dev = sis_fbdev->helper.dev; + struct sisvga_device *sis_dev = dev->dev_private; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = mode_cmd.pitches[0] * mode_cmd.height; + + sysram = vmalloc(size); + if (!sysram) + return -ENOMEM; + + ret = sisvga_gem_create(dev, size, true, &gem_obj); + if (ret) + goto err_sisvga_gem_create; + + sis_fb = sisvga_framebuffer_create(dev, gem_obj, &mode_cmd); + if (IS_ERR(sis_fb)) { + ret = PTR_ERR(sis_fb); + goto err_sisvga_framebuffer_create; + } + sis_fbdev->fb = sis_fb; + + /* setup helper */ + sis_fbdev->helper.fb = &sis_fb->base; + + info = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(info)) { + ret = PTR_ERR(info); + goto err_drm_fb_helper_alloc_fbi; + } + + strcpy(info->fix.id, "sisvga-fb"); + info->par = sis_fbdev; + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; + info->fbops = &sisvga_fb_ops; + /* setup aperture base/size for vesafb takeover */ + info->apertures->ranges[0].base = dev->mode_config.fb_base; + info->apertures->ranges[0].size = sis_dev->vram.size; + drm_fb_helper_fill_fix(info, sis_fb->base.pitches[0], + sis_fb->base.format->depth); + drm_fb_helper_fill_var(info, &sis_fbdev->helper, sizes->fb_width, + sizes->fb_height); + info->screen_base = sysram; + info->screen_size = size; + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + drm_gem_object_put_unlocked(gem_obj); + + return 0; + +err_drm_fb_helper_alloc_fbi: + drm_framebuffer_put(&sis_fb->base); +err_sisvga_framebuffer_create: + drm_gem_object_put_unlocked(gem_obj); +err_sisvga_gem_create: + vfree(sysram); + return ret; +} + +static const struct drm_fb_helper_funcs sisvga_fb_helper_funcs = { + .fb_probe = sisvga_fb_helper_fb_probe, +}; + +/* + * struct sisvga_fbdev + */ + +int sisvga_fbdev_init(struct sisvga_fbdev *sis_fbdev, + struct drm_device *dev, int bpp) +{ + int ret; + + sis_fbdev->x1 = sis_fbdev->y1 = INT_MAX; + sis_fbdev->x2 = sis_fbdev->y2 = 0; + spin_lock_init(&sis_fbdev->dirty_lock); + + drm_fb_helper_prepare(dev, &sis_fbdev->helper, + &sisvga_fb_helper_funcs); + + ret = drm_fb_helper_init(dev, &sis_fbdev->helper, 1); + if (ret < 0) { + DRM_ERROR("sisvga: drm_fb_helper_init() failed," + " error %d\n", -ret); + return ret; + } + + ret = drm_fb_helper_single_add_all_connectors(&sis_fbdev->helper); + if (ret < 0) { + DRM_ERROR("drm_fb_helper_single_add_all_connectors failed: %d", -ret); + return ret; + } + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(&sis_fbdev->helper, bpp); + if (ret < 3) { + DRM_ERROR("drm_fb_helper_initial_config failed: %d", -ret); + return ret; + } + + return 0; +} + +void sisvga_fbdev_fini(struct sisvga_fbdev *sis_fbdev) +{ + void *sysram; + struct sisvga_framebuffer *sis_fb = sis_fbdev->fb; + + sysram = sis_fbdev->helper.fbdev->screen_base; + + drm_fb_helper_unregister_fbi(&sis_fbdev->helper); + drm_fb_helper_fini(&sis_fbdev->helper); + + drm_framebuffer_put(&sis_fb->base); + + if (sysram) + vfree(sysram); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_framebuffer.c b/drivers/gpu/drm/sisvga/sisvga_framebuffer.c new file mode 100644 index 0000000000000..3349158b7aad9 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_framebuffer.c @@ -0,0 +1,93 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> /* include before drm/drm_gem.h */ +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem.h> + +/* + * Framebuffer helpers + */ + +static void sisvga_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct sisvga_framebuffer *sis_fb = sisvga_framebuffer_of(fb); + struct drm_device *dev = fb->dev; + struct drm_gem_object *gem_obj = sis_fb->gem_obj; + + drm_framebuffer_cleanup(&sis_fb->base); + devm_kfree(dev->dev, sis_fb); + + if (gem_obj) + drm_gem_object_put_unlocked(gem_obj); +} + +static const struct drm_framebuffer_funcs sisvga_framebuffer_funcs = { + .destroy = sisvga_framebuffer_destroy, +}; + +/* + * struct sisvga_framebuffer + */ + +static int sisvga_framebuffer_init(struct sisvga_framebuffer *sis_fb, + struct drm_device *dev, + struct drm_gem_object *gem_obj, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int ret; + + if (gem_obj) + drm_gem_object_get(gem_obj); + sis_fb->gem_obj = gem_obj; + + drm_helper_mode_fill_fb_struct(dev, &sis_fb->base, mode_cmd); + + ret = drm_framebuffer_init(dev, &sis_fb->base, + &sisvga_framebuffer_funcs); + if (ret < 0) { + DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); + goto err_drm_framebuffer_init; + } + + return 0; + +err_drm_framebuffer_init: + if (gem_obj) + drm_gem_object_put_unlocked(gem_obj); + return ret; +} + +struct sisvga_framebuffer* sisvga_framebuffer_create( + struct drm_device *dev, + struct drm_gem_object *gem_obj, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct sisvga_framebuffer *sis_fb; + int ret; + + sis_fb = devm_kzalloc(dev->dev, sizeof(*sis_fb), GFP_KERNEL); + if (!sis_fb) + return ERR_PTR(-ENOMEM); + + ret = sisvga_framebuffer_init(sis_fb, dev, gem_obj, mode_cmd); + if (ret) + goto err_sisvga_framebuffer_init; + + return sis_fb; + +err_sisvga_framebuffer_init: + devm_kfree(dev->dev, sis_fb); + return ERR_PTR(ret); +} + +struct sisvga_framebuffer* sisvga_framebuffer_of(struct drm_framebuffer *fb) +{ + return container_of(fb, struct sisvga_framebuffer, base); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_modes.c b/drivers/gpu/drm/sisvga/sisvga_modes.c new file mode 100644 index 0000000000000..2c129bd656711 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_modes.c @@ -0,0 +1,42 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_modes.h" + +static bool +depth_is_compatible(const struct sisvga_mode* smode, int bpp) +{ + return (smode->depth == bpp) || ((smode->depth == 24) && (bpp == 32)); +} + +bool +sisvga_mode_is_compatible(const struct sisvga_mode* smode, + const struct drm_display_mode* mode, + int bpp) +{ + return (smode->hdisplay == mode->hdisplay) && + (smode->vdisplay == mode->vdisplay) && + (smode->clock == mode->clock) && + (depth_is_compatible(smode, bpp)); + +} + +const struct sisvga_mode* +sisvga_find_compatible_mode(const struct sisvga_mode* beg, + const struct sisvga_mode* end, + const struct drm_display_mode* mode, + int bpp) +{ + while (beg < end) { + if (sisvga_mode_is_compatible(beg, mode, bpp)) { + return beg; + } + ++beg; + } + return NULL; +} diff --git a/drivers/gpu/drm/sisvga/sisvga_modes.h b/drivers/gpu/drm/sisvga/sisvga_modes.h new file mode 100644 index 0000000000000..91f55e4f99e31 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_modes.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#ifndef SISVGA_MODE_H +#define SISVGA_MODE_H + +#include "sisvga_vclk.h" +#include <drm/drm_modes.h> + +struct sisvga_mode { + int hdisplay; + int vdisplay; + int depth; + int clock; /* Pixel clock in KHz */ + int min_vram; /* Minimally required VRAM in KiB */ + int flags; + enum sisvga_vclk pixel_clock; +}; + +#define SISVGA_MODE(name_, hdisplay_, vdisplay_, frame_rate_, depth_,\ + clock_, min_vram_, flags_)\ + .hdisplay = (hdisplay_),\ + .vdisplay = (vdisplay_),\ + .depth = (depth_),\ + .clock = (clock_),\ + .min_vram = (min_vram_),\ + .flags = (flags_),\ + .pixel_clock = (SISVGA_VCLK_ ## clock_) + +bool +sisvga_mode_is_compatible(const struct sisvga_mode* smode, + const struct drm_display_mode* mode, + int bpp); + +const struct sisvga_mode* +sisvga_find_compatible_mode(const struct sisvga_mode* beg, + const struct sisvga_mode* end, + const struct drm_display_mode* mode, + int bpp); + +#endif diff --git a/drivers/gpu/drm/sisvga/sisvga_plane.c b/drivers/gpu/drm/sisvga/sisvga_plane.c new file mode 100644 index 0000000000000..86d7cbf6417a3 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_plane.c @@ -0,0 +1,127 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" + +static struct sisvga_plane* sisvga_plane_of(struct drm_plane* plane) +{ + return container_of(plane, struct sisvga_plane, base); +} + +/* + * Plane funcs + */ + +static int sisvga_plane_funcs_update_plane(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) +{ + return 0; +} + +static int sisvga_plane_funcs_disable_plane( + struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx) +{ + return 0; +} + +static void sisvga_plane_funcs_destroy(struct drm_plane *plane) +{ + struct sisvga_plane* sis_plane = sisvga_plane_of(plane); + struct drm_device *dev = plane->dev; + + drm_plane_cleanup(&sis_plane->base); + devm_kfree(dev->dev, sis_plane); +} + +static int sisvga_plane_funcs_set_property(struct drm_plane *plane, + struct drm_property *property, + uint64_t value) +{ + DRM_INFO("property: %s\n", property->name); + + return 0; +} + +static const struct drm_plane_funcs sisvga_plane_funcs = { + .update_plane = sisvga_plane_funcs_update_plane, + .disable_plane = sisvga_plane_funcs_disable_plane, + .destroy = sisvga_plane_funcs_destroy, + .set_property = sisvga_plane_funcs_set_property, +}; + +/* + * struct sisvga_plane + */ + +static int sisvga_plane_init(struct sisvga_plane* sis_plane, + struct drm_device *dev, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type) +{ + int ret; + + ret = drm_universal_plane_init(dev, &sis_plane->base, 0, + &sisvga_plane_funcs, formats, + format_count, format_modifiers, type, + NULL); + if (ret) + return ret; + + ret = drm_plane_create_zpos_immutable_property(&sis_plane->base, 0); + if (ret) { + DRM_ERROR("%s:%d %d\n", __func__, __LINE__, -ret); + goto err; + } + + ret = drm_plane_create_rotation_property(&sis_plane->base, + DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0); + if (ret) { + DRM_ERROR("%s:%d %d\n", __func__, __LINE__, -ret); + goto err; + } + + return 0; + +err: + drm_plane_cleanup(&sis_plane->base); + return ret; +} + +struct sisvga_plane* sisvga_plane_create(struct drm_device *dev, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type) +{ + struct sisvga_plane* sis_plane; + int ret; + + sis_plane = devm_kzalloc(dev->dev, sizeof(*sis_plane), GFP_KERNEL); + if (!sis_plane) + return ERR_PTR(-ENOMEM); + + ret = sisvga_plane_init(sis_plane, dev, formats, format_count, + format_modifiers, type); + if (ret) + goto err_sisvga_plane_init; + + return sis_plane; + +err_sisvga_plane_init: + devm_kfree(dev->dev, sis_plane); + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_ttm.c b/drivers/gpu/drm/sisvga/sisvga_ttm.c new file mode 100644 index 0000000000000..2dd50ff326d68 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_ttm.c @@ -0,0 +1,275 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Authors: Thomas Zimmermann + */ + +#include "sisvga_device.h" +#include <drm/drmP.h> +#include <drm/ttm/ttm_page_alloc.h> +#include "sisvga_reg.h" + +#undef VRAM_TTM_MEM +#define VRAM_TTM_MEM(__ttm) ((__ttm)->mem_global_ref) + +#undef VRAM_TTM_BO +#define VRAM_TTM_BO(__ttm) ((__ttm)->bo_global_ref) + +#undef VRAM_TTM_BO_DEV +#define VRAM_TTM_BO_DEV(__ttm) ((__ttm)->bdev) + +static struct sisvga_device* sisvga_device_of_bo_device( + struct ttm_bo_device *bdev) +{ + return container_of(bdev, struct sisvga_device, ttm.bdev); +} + +static struct sisvga_bo* sisvga_bo_of_ttm_buffer_object( + struct ttm_buffer_object *bo) +{ + return container_of(bo, struct sisvga_bo, bo); +} + +/* + * TTM global memory + */ + +static int sisvga_global_ttm_mem_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void sisvga_global_ttm_mem_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +static int sisvga_init_ttm_mem(struct sisvga_ttm *ttm) +{ + int ret; + + VRAM_TTM_MEM(ttm).global_type = DRM_GLOBAL_TTM_MEM; + VRAM_TTM_MEM(ttm).size = sizeof(struct ttm_mem_global); + VRAM_TTM_MEM(ttm).init = &sisvga_global_ttm_mem_init; + VRAM_TTM_MEM(ttm).release = &sisvga_global_ttm_mem_release; + + ret = drm_global_item_ref(&VRAM_TTM_MEM(ttm)); + if (ret < 0) { + DRM_ERROR("sisvga: setup of TTM memory accounting failed\n"); + return ret; + } + + return 0; +} + +/* + * TTM global BO + */ + +static int sisvga_init_ttm_bo(struct sisvga_ttm* ttm) +{ + int ret; + + VRAM_TTM_BO(ttm).mem_glob = VRAM_TTM_MEM(ttm).object; + VRAM_TTM_BO(ttm).ref.global_type = DRM_GLOBAL_TTM_BO; + VRAM_TTM_BO(ttm).ref.size = sizeof(struct ttm_bo_global); + VRAM_TTM_BO(ttm).ref.init = &ttm_bo_global_init; + VRAM_TTM_BO(ttm).ref.release = &ttm_bo_global_release; + + ret = drm_global_item_ref(&VRAM_TTM_BO(ttm).ref); + if (ret < 0) { + DRM_ERROR("sisvga: setup of TTM BO failed\n"); + return ret; + } + + return 0; +} + +/* + * TTM BO device + */ + +static void sisvga_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func sisvga_ttm_backend_func = { + .destroy = sisvga_ttm_backend_destroy +}; + +static struct ttm_tt *sisvga_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (tt == NULL) + return NULL; + + tt->func = &sisvga_ttm_backend_func; + + ret = ttm_tt_init(tt, bo, page_flags); + if (ret < 0) { + kfree(tt); + return NULL; + } + + return tt; +} + +static int sisvga_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("sisvga: Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void sisvga_bo_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *pl) +{ + /* TODO */ + DRM_INFO("%s:%d TODO\n", __func__, __LINE__); +} + +static int sisvga_bo_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + struct sisvga_bo *sis_bo = sisvga_bo_of_ttm_buffer_object(bo); + + return drm_vma_node_verify_access(&sis_bo->gem.vma_node, + filp->private_data); +} + +static int sisvga_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; + struct sisvga_device *sdev = sisvga_device_of_bo_device(bdev); + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + + mem->bus.addr = NULL; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: /* nothing to do */ + mem->bus.offset = 0; + mem->bus.base = 0; + mem->bus.is_iomem = false; + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = sdev->vram.base; // pci_resource_start(mdev->dev->pdev, 0); + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void sisvga_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + /* TODO */ + DRM_INFO("%s:%d TODO\n", __func__, __LINE__); +} + +static struct ttm_bo_driver sisvga_bo_driver = { + .ttm_tt_create = sisvga_ttm_tt_create, + .ttm_tt_populate = ttm_pool_populate, + .ttm_tt_unpopulate = ttm_pool_unpopulate, + .init_mem_type = sisvga_bo_init_mem_type, + .evict_flags = sisvga_bo_evict_flags, + .verify_access = sisvga_bo_verify_access, + .io_mem_reserve = sisvga_ttm_io_mem_reserve, + .io_mem_free = sisvga_ttm_io_mem_free, +}; + +static int sisvga_init_ttm_bo_device(struct sisvga_ttm *ttm, + struct drm_device *dev, + unsigned long p_size) +{ + int ret; + + ret = ttm_bo_device_init(&VRAM_TTM_BO_DEV(ttm), + VRAM_TTM_BO(ttm).ref.object, + &sisvga_bo_driver, + dev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, + true); + if (ret) { + DRM_ERROR("sisvga: ttm_bo_device_init failed; %d\n", ret); + return ret; + } + + ret = ttm_bo_init_mm(&VRAM_TTM_BO_DEV(ttm), TTM_PL_VRAM, p_size); + if (ret) { + DRM_ERROR("sisvga: ttm_bo_init_mm failed: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * struct sisvga_ttm + */ + +int sisvga_ttm_init(struct sisvga_ttm *ttm, + struct drm_device *dev, + unsigned long p_size) +{ + int ret; + + ret = sisvga_init_ttm_mem(ttm); + if (ret < 0) + return ret; + + ret = sisvga_init_ttm_bo(ttm); + if (ret < 0) + goto err_init_ttm_bo; + + ret = sisvga_init_ttm_bo_device(ttm, dev, p_size); + if (ret < 0) + goto err_init_ttm_bo_device; + + return 0; + +err_init_ttm_bo_device: + ttm_mem_global_release(VRAM_TTM_BO(ttm).ref.object); +err_init_ttm_bo: + ttm_mem_global_release(VRAM_TTM_MEM(ttm).object); + return ret; +} + +void sisvga_ttm_fini(struct sisvga_ttm *ttm) +{ + ttm_bo_device_release(&VRAM_TTM_BO_DEV(ttm)); + ttm_mem_global_release(VRAM_TTM_BO(ttm).ref.object); + ttm_mem_global_release(VRAM_TTM_MEM(ttm).object); +} diff --git a/drivers/gpu/drm/sisvga/sisvga_vclk.c b/drivers/gpu/drm/sisvga/sisvga_vclk.c new file mode 100644 index 0000000000000..fd1fd5b9b5c69 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_vclk.c @@ -0,0 +1,167 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + */ + +#include "sisvga_vclk.h" +#include <linux/bug.h> +#include <linux/errno.h> +#include <linux/kernel.h> + +struct sisvga_clk_config { + enum sisvga_freq freq; + unsigned char num; + unsigned char denum; + unsigned char div; + unsigned char postscal; + int clock_hz; +}; + +#define SISVGA_CLK_CONFIG(description, f, n, dn, d, p, c) \ + .freq = (SISVGA_FREQ_ ## f), \ + .num = (n), \ + .denum = (dn), \ + .div = (d), \ + .postscal = (p), \ + .clock_hz = (c) + +/* On all SiS adapters we have to configure the internal dot-clock + * generator. According to its manual on the SiS 6326 we can configure + * VGA clock generators in the same way. In practice hardware doesn't + * support this. So we only use VGA registers for VGA dot clocks, and + * extended registers for the internal clock generator. + * + * The config table for the SiS 6326 replaces VGA clock generators, + * with the internal one, even if the VGA clock generator would produce + * better results. + */ +static const struct sisvga_clk_config sisvga_vclk_configs[] = { + /* Any VGA */ + { SISVGA_CLK_CONFIG( "25.175 MHz", 25175, 1, 1, 1, 1, 25175000) }, + { SISVGA_CLK_CONFIG( "28.322 MHz", 28322, 1, 1, 1, 1, 28322000) }, + /* SiS 6202 and later */ + { SISVGA_CLK_CONFIG( "30.000 MHz", 14318, 22, 21, 2, 1, 29999996) }, + { SISVGA_CLK_CONFIG( "31.500 MHz", 14318, 11, 5, 1, 1, 31499996) }, + //{ SISVGA_CLK_CONFIG( "36.000 MHz", 25175, 5, 7, 2, 1, 35964285) }, + { SISVGA_CLK_CONFIG( "36.000 MHz", 14318, 83, 11, 1, 3, 36012392) }, + { SISVGA_CLK_CONFIG( "40.000 MHz", 14318, 88, 21, 2, 3, 39999994) }, + //{ SISVGA_CLK_CONFIG( "44.889 MHz", 25175, 41, 23, 1, 1, 44877173) }, + { SISVGA_CLK_CONFIG( "44.889 MHz", 14318, 47, 15, 1, 1, 44863630) }, + { SISVGA_CLK_CONFIG( "44.900 MHz", 14318, 127, 27, 2, 3, 44898984) }, + { SISVGA_CLK_CONFIG( "50.000 MHz", 14318, 110, 21, 2, 3, 49999993) }, + { SISVGA_CLK_CONFIG( "56.300 MHz", 14318, 59, 15, 1, 1, 56318174) }, + { SISVGA_CLK_CONFIG( "65.000 MHz", 14318, 59, 13, 1, 1, 64982509) }, + { SISVGA_CLK_CONFIG( "75.000 MHz", 14318, 55, 21, 2, 1, 74999990) }, + { SISVGA_CLK_CONFIG( "77.000 MHz", 14318, 121, 15, 2, 3, 76999990) }, + { SISVGA_CLK_CONFIG( "80.000 MHz", 14318, 95, 17, 1, 1, 80013358) }, + { SISVGA_CLK_CONFIG( "94.500 MHz", 14318, 33, 5, 1, 1, 94499988) }, + { SISVGA_CLK_CONFIG("110.000 MHz", 14318, 73, 19, 2, 1, 110023909) }, + { SISVGA_CLK_CONFIG("120.000 MHz", 14318, 88, 21, 2, 1, 119999984) }, + { SISVGA_CLK_CONFIG("130.000 MHz", 14318, 59, 13, 2, 1, 129965018) }, + /* SiS 6215 and later */ + { SISVGA_CLK_CONFIG("135.000 MHz", 14318, 33, 7, 2, 1, 134999982) }, + /* SiS 6326 and later */ + //{ SISVGA_CLK_CONFIG("162.000 MHz", 25175, 74, 23, 2, 1, 161995652) }, + { SISVGA_CLK_CONFIG("162.000 MHz", 14318, 17, 3, 2, 1, 162272706) }, + { SISVGA_CLK_CONFIG("175.500 MHz", 14318, 49, 4, 1, 1, 175397705) }, +}; + +int sisvga_vclk_of_clock(unsigned int clock_khz, enum sisvga_vclk *vclk_out) +{ + switch (clock_khz) { + case 25000: /* fall through */ + case 25175: + *vclk_out = SISVGA_VCLK_25175; + return 0; + case 28000: /* fall through */ + case 28322: + *vclk_out = SISVGA_VCLK_28322; + return 0; + case 30000: + *vclk_out = SISVGA_VCLK_30000; + return 0; + case 31500: + *vclk_out = SISVGA_VCLK_31500; + return 0; + case 36000: + *vclk_out = SISVGA_VCLK_36000; + return 0; + case 40000: + *vclk_out = SISVGA_VCLK_40000; + return 0; + case 44889: + *vclk_out = SISVGA_VCLK_44889; + return 0; + case 44900: + *vclk_out = SISVGA_VCLK_44900; + return 0; + case 50000: + *vclk_out = SISVGA_VCLK_50000; + return 0; + case 56300: + *vclk_out = SISVGA_VCLK_56300; + return 0; + case 65000: + *vclk_out = SISVGA_VCLK_65000; + return 0; + case 75000: + *vclk_out = SISVGA_VCLK_75000; + return 0; + case 77000: + *vclk_out = SISVGA_VCLK_77000; + return 0; + case 80000: + *vclk_out = SISVGA_VCLK_80000; + return 0; + case 94500: + *vclk_out = SISVGA_VCLK_94500; + return 0; + case 110000: + *vclk_out = SISVGA_VCLK_110000; + return 0; + case 120000: + *vclk_out = SISVGA_VCLK_120000; + return 0; + case 130000: + *vclk_out = SISVGA_VCLK_130000; + return 0; + case 135000: + *vclk_out = SISVGA_VCLK_135000; + return 0; + case 162000: + *vclk_out = SISVGA_VCLK_162000; + return 0; + case 175500: + *vclk_out = SISVGA_VCLK_175500; + return 0; + default: + return -EINVAL; + } +} + +void sisvga_vclk_regs(enum sisvga_vclk vclk, enum sisvga_freq *freq_out, + unsigned long *num_out, + unsigned long *denum_out, + unsigned long *div_out, + unsigned long *postscal_out, + unsigned long *f_out) +{ + BUG_ON(!(vclk < ARRAY_SIZE(sisvga_vclk_configs))); + BUG_ON(sisvga_vclk_configs[vclk].freq > 2); + BUG_ON(sisvga_vclk_configs[vclk].num < 1); + BUG_ON(sisvga_vclk_configs[vclk].num > 128); + BUG_ON(sisvga_vclk_configs[vclk].denum < 1); + BUG_ON(sisvga_vclk_configs[vclk].denum > 32); + BUG_ON(sisvga_vclk_configs[vclk].div < 1); + BUG_ON(sisvga_vclk_configs[vclk].div > 2); + BUG_ON(sisvga_vclk_configs[vclk].postscal < 1); + BUG_ON(sisvga_vclk_configs[vclk].postscal > 8); + + *freq_out = sisvga_vclk_configs[vclk].freq; + *num_out = sisvga_vclk_configs[vclk].num; + *denum_out = sisvga_vclk_configs[vclk].denum; + *div_out = sisvga_vclk_configs[vclk].div; + *postscal_out = sisvga_vclk_configs[vclk].postscal; + *f_out = sisvga_vclk_configs[vclk].clock_hz; +} diff --git a/drivers/gpu/drm/sisvga/sisvga_vclk.h b/drivers/gpu/drm/sisvga/sisvga_vclk.h new file mode 100644 index 0000000000000..b44fb9518e234 --- /dev/null +++ b/drivers/gpu/drm/sisvga/sisvga_vclk.h @@ -0,0 +1,57 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + */ + +#ifndef SISVGA_VCLK_H +#define SISVGA_VCLK_H + +enum sisvga_freq { + SISVGA_FREQ_14318 = 0, /* from SiS internal clock generator */ + SISVGA_FREQ_25175, + SISVGA_FREQ_28322 +}; + +enum sisvga_vclk { + /* Any VGA */ + SISVGA_VCLK_25175, + SISVGA_VCLK_28322, + /* SiS 6202 and later */ + SISVGA_VCLK_30000, + SISVGA_VCLK_31500, + SISVGA_VCLK_36000, + SISVGA_VCLK_40000, + SISVGA_VCLK_44889, + SISVGA_VCLK_44900, + SISVGA_VCLK_50000, + SISVGA_VCLK_56300, + SISVGA_VCLK_65000, + SISVGA_VCLK_75000, + SISVGA_VCLK_77000, + SISVGA_VCLK_80000, + SISVGA_VCLK_94500, + SISVGA_VCLK_110000, + SISVGA_VCLK_120000, + SISVGA_VCLK_130000, + /* SiS 6215 and later */ + SISVGA_VCLK_135000, + /* SiS 6326 and later */ + SISVGA_VCLK_162000, + SISVGA_VCLK_175500 +}; + +#define KHZ_TO_HZ(_khz) ((_khz) * 1000) +#define MHZ_TO_KHZ(_mhz) ((_mhz) * 1000) +#define MHZ_TO_HZ(_mhz) KHZ_TO_HZ(MHZ_TO_KHZ(_mhz)) + +int sisvga_vclk_of_clock(unsigned int clock_khz, enum sisvga_vclk *vclk_out); + +void sisvga_vclk_regs(enum sisvga_vclk vclk, enum sisvga_freq *freq_out, + unsigned long *num_out, + unsigned long *denum_out, + unsigned long *div_out, + unsigned long *postscal_out, + unsigned long *f_out); + +#endif diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 29502238e5107..d474288b8e17b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -684,6 +684,8 @@ #define PCI_DEVICE_ID_SI_LPC 0x0018 #define PCI_DEVICE_ID_SI_5597_VGA 0x0200 #define PCI_DEVICE_ID_SI_6205 0x0205 +#define PCI_DEVICE_ID_SI_6215 0x0204 +#define PCI_DEVICE_ID_SI_6326 0x6326 #define PCI_DEVICE_ID_SI_501 0x0406 #define PCI_DEVICE_ID_SI_496 0x0496 #define PCI_DEVICE_ID_SI_300 0x0300 -- GitLab