(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
* DRM stub code that calls into fbconv helpers, and * the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
v2: * rename to fbconv helpers * rewrite as helper library * switch over to simple KMS helpers * switch over to SHMEM * add documentation
[1] https://gitlab.freedesktop.org/tzimmermann/linux/commits/fbconv-plus-drivers
Thomas Zimmermann (15): fbdev: Export fb_check_foreignness() fbdev: Export FBPIXMAPSIZE drm/simple-kms-helper: Add mode_fixup() to simple display pipe drm: Add fbconv helper module drm/fbconv: Add DRM <-> fbdev pixel-format conversion drm/fbconv: Add mode conversion DRM <-> fbdev drm/fbconv: Add modesetting infrastructure drm/fbconv: Add plane-state check and update drm/fbconv: Mode-setting pipeline enable / disable drm/fbconv: Reimplement several fbdev interfaces drm/fbconv: Add helpers for init and cleanup of fb_info structures drm/fbconv: Add helper documentation staging: Add mgakms driver staging/mgakms: Import matroxfb driver source code staging/mgakms: Update matroxfb driver code for DRM
Documentation/gpu/drm-kms-helpers.rst | 12 + Documentation/gpu/todo.rst | 15 + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 2126 +++++++++++++++++ drivers/gpu/drm/drm_simple_kms_helper.c | 15 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/mgakms/Kconfig | 18 + drivers/staging/mgakms/Makefile | 17 + drivers/staging/mgakms/g450_pll.c | 539 +++++ drivers/staging/mgakms/g450_pll.h | 13 + drivers/staging/mgakms/i2c-matroxfb.c | 238 ++ drivers/staging/mgakms/matroxfb_DAC1064.c | 1082 +++++++++ drivers/staging/mgakms/matroxfb_DAC1064.h | 174 ++ drivers/staging/mgakms/matroxfb_Ti3026.c | 746 ++++++ drivers/staging/mgakms/matroxfb_Ti3026.h | 10 + drivers/staging/mgakms/matroxfb_accel.c | 519 +++++ drivers/staging/mgakms/matroxfb_accel.h | 9 + drivers/staging/mgakms/matroxfb_base.c | 2592 +++++++++++++++++++++ drivers/staging/mgakms/matroxfb_base.h | 700 ++++++ drivers/staging/mgakms/matroxfb_crtc2.h | 35 + drivers/staging/mgakms/matroxfb_g450.c | 640 +++++ drivers/staging/mgakms/matroxfb_g450.h | 10 + drivers/staging/mgakms/matroxfb_maven.h | 21 + drivers/staging/mgakms/matroxfb_misc.c | 815 +++++++ drivers/staging/mgakms/matroxfb_misc.h | 22 + drivers/staging/mgakms/mga_device.c | 68 + drivers/staging/mgakms/mga_device.h | 30 + drivers/staging/mgakms/mga_drv.c | 129 + drivers/staging/mgakms/mga_drv.h | 14 + drivers/video/fbdev/core/fbmem.c | 5 +- include/drm/drm_fbconv_helper.h | 150 ++ include/drm/drm_simple_kms_helper.h | 43 + include/linux/fb.h | 3 + 35 files changed, 10822 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/drm_fbconv_helper.c create mode 100644 drivers/staging/mgakms/Kconfig create mode 100644 drivers/staging/mgakms/Makefile create mode 100644 drivers/staging/mgakms/g450_pll.c create mode 100644 drivers/staging/mgakms/g450_pll.h create mode 100644 drivers/staging/mgakms/i2c-matroxfb.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.h create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.c create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.h create mode 100644 drivers/staging/mgakms/matroxfb_accel.c create mode 100644 drivers/staging/mgakms/matroxfb_accel.h create mode 100644 drivers/staging/mgakms/matroxfb_base.c create mode 100644 drivers/staging/mgakms/matroxfb_base.h create mode 100644 drivers/staging/mgakms/matroxfb_crtc2.h create mode 100644 drivers/staging/mgakms/matroxfb_g450.c create mode 100644 drivers/staging/mgakms/matroxfb_g450.h create mode 100644 drivers/staging/mgakms/matroxfb_maven.h create mode 100644 drivers/staging/mgakms/matroxfb_misc.c create mode 100644 drivers/staging/mgakms/matroxfb_misc.h create mode 100644 drivers/staging/mgakms/mga_device.c create mode 100644 drivers/staging/mgakms/mga_device.h create mode 100644 drivers/staging/mgakms/mga_drv.c create mode 100644 drivers/staging/mgakms/mga_drv.h create mode 100644 include/drm/drm_fbconv_helper.h
-- 2.23.0
This function is required by DRM's fbdev conversion helpers. Export it from the module.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/video/fbdev/core/fbmem.c | 3 ++- include/linux/fb.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 95c32952fa8a..e828fcccce40 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1481,7 +1481,7 @@ static const struct file_operations fb_fops = { struct class *fb_class; EXPORT_SYMBOL(fb_class);
-static int fb_check_foreignness(struct fb_info *fi) +int fb_check_foreignness(struct fb_info *fi) { const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
@@ -1505,6 +1505,7 @@ static int fb_check_foreignness(struct fb_info *fi)
return 0; } +EXPORT_SYMBOL(fb_check_foreignness);
static bool apertures_overlap(struct aperture *gen, struct aperture *hw) { diff --git a/include/linux/fb.h b/include/linux/fb.h index 41e0069eca0a..372f1f6ae42e 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -622,6 +622,7 @@ extern int fb_get_color_depth(struct fb_var_screeninfo *var, struct fb_fix_screeninfo *fix); extern int fb_get_options(const char *name, char **option); extern int fb_new_modelist(struct fb_info *info); +extern int fb_check_foreignness(struct fb_info *fi);
extern struct fb_info *registered_fb[FB_MAX]; extern int num_registered_fb;
This constant is required by DRM's fbdev conversion helpers. Define it in fbdev's public header file.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/video/fbdev/core/fbmem.c | 2 -- include/linux/fb.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index e828fcccce40..f02377e959dc 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -43,8 +43,6 @@ * Frame buffer device initialization and setup routines */
-#define FBPIXMAPSIZE (1024 * 8) - static DEFINE_MUTEX(registration_lock);
struct fb_info *registered_fb[FB_MAX] __read_mostly; diff --git a/include/linux/fb.h b/include/linux/fb.h index 372f1f6ae42e..e17d3e1d86ad 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -199,6 +199,8 @@ struct fb_pixmap { void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size); };
+#define FBPIXMAPSIZE (1024 * 8) + #ifdef CONFIG_FB_DEFERRED_IO struct fb_deferred_io { /* delay between mkwrite and deferred handler */
The mode fix-up function for simple display helpers is equivalent to the regular pipeline's CRTC mode fix-up function. It's called to adjust the CRTC's display mode for the encoder. Add this function for DRM fbconv helpers.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_simple_kms_helper.c | 15 +++++++++ include/drm/drm_simple_kms_helper.h | 43 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 046055719245..acd9b79bf92a 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -46,6 +46,20 @@ drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc, return pipe->funcs->mode_valid(crtc, mode); }
+static bool +drm_simple_kms_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_simple_display_pipe *pipe; + + pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); + if (!pipe->funcs || !pipe->funcs->mode_fixup) + return true; + + return pipe->funcs->mode_fixup(crtc, mode, adjusted_mode); +} + static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -87,6 +101,7 @@ static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { .mode_valid = drm_simple_kms_crtc_mode_valid, + .mode_fixup = drm_simple_kms_crtc_mode_fixup, .atomic_check = drm_simple_kms_crtc_check, .atomic_enable = drm_simple_kms_crtc_enable, .atomic_disable = drm_simple_kms_crtc_disable, diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 4d89cd0a60db..1b975ab67144 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -52,6 +52,49 @@ struct drm_simple_display_pipe_funcs { enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc, const struct drm_display_mode *mode);
+ /** + * @mode_fixup: + * + * This callback is used to validate a mode. The parameter mode is the + * display mode that userspace requested, adjusted_mode is the mode the + * encoders need to be fed with. Note that this is the inverse semantics + * of the meaning for the &drm_encoder and &drm_bridge_funcs.mode_fixup + * vfunc. If the CRTC of the simple display pipe cannot support the + * requested conversion from mode to adjusted_mode it should reject the + * modeset. + * + * This function is optional. + * + * NOTE: + * + * This function is called in the check phase of atomic modesets, which + * can be aborted for any reason (including on userspace's request to + * just check whether a configuration would be possible). Atomic drivers + * MUST NOT touch any persistent state (hardware or software) or data + * structures except the passed in adjusted_mode parameter. + * + * Atomic drivers which need to inspect and adjust more state should + * instead use the @atomic_check callback, but note that they're not + * perfectly equivalent: @mode_valid is called from + * drm_atomic_helper_check_modeset(), but @atomic_check is called from + * drm_atomic_helper_check_planes(), because originally it was meant for + * plane update checks only. + * + * Also beware that userspace can request its own custom modes, neither + * core nor helpers filter modes to the list of probe modes reported by + * the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure + * that modes are filtered consistently put any CRTC constraints and + * limits checks into @mode_valid. + * + * RETURNS: + * + * True if an acceptable configuration is possible, false if the modeset + * operation should be rejected. + */ + bool (*mode_fixup)(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + /** * @enable: *
This adds fbconv helpers for DRM to the build infrastructure. The configuration symbol is DRM_FBCONV_HELPERS. Drivers should select it if they wish to use fbconv.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 10 ++++++++++ drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 1 + 3 files changed, 12 insertions(+) create mode 100644 drivers/gpu/drm/drm_fbconv_helper.c
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 9591bd720e56..ed689201ec81 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -157,6 +157,16 @@ config DRM_DP_CEC Note: not all adapters support this feature, and even for those that do support this they often do not hook up the CEC pin.
+config DRM_FBCONV_HELPER + tristate "Enable fbdev conversion helpers" + depends on DRM + help + Provides helpers for running DRM on top of fbdev drivers. Choose + this option if you're converting an fbdev driver to DRM. The + helpers provide conversion functions for fbdev data structures + and allow to build a basic DRM driver on top of the fbdev + interfaces. + config DRM_TTM tristate depends on DRM && MMU diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 9f1c7c486f88..a7178245d938 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -52,6 +52,7 @@ drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o drm_kms_helper-$(CONFIG_DRM_DP_AUX_CHARDEV) += drm_dp_aux_dev.o drm_kms_helper-$(CONFIG_DRM_DP_CEC) += drm_dp_cec.o +drm_kms_helper-$(CONFIG_DRM_FBCONV_HELPER) += drm_fbconv_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/ diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c new file mode 100644 index 000000000000..0cb46d2c98c3 --- /dev/null +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -0,0 +1 @@ +// SPDX-License-Identifier: GPL-2.0-or-later
DRM uses FOURCC constants to describe pixel formats, fbdev uses a per-component bitfield structure. The functions in this patch convert between the two.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 435 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 23 ++ 2 files changed, 458 insertions(+) create mode 100644 include/drm/drm_fbconv_helper.h
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index 0cb46d2c98c3..af45358a156a 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -1 +1,436 @@ // SPDX-License-Identifier: GPL-2.0-or-later + +#include <asm/byteorder.h> + +#include <linux/fb.h> + +#include <drm/drm_fbconv_helper.h> + +/* + * Format conversion helpers + */ + +static bool is_c8(const struct fb_var_screeninfo *fb_var) +{ + return fb_var->bits_per_pixel == 8; +} + +static bool is_rgb565(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 16) && + (fb_var->red.offset == 11) && + (fb_var->red.length == 5) && + (fb_var->green.offset == 5) && + (fb_var->green.length == 6) && + (fb_var->blue.offset == 0) && + (fb_var->blue.length == 5); +} + +static bool is_bgr565(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 16) && + (fb_var->red.offset == 0) && + (fb_var->red.length == 5) && + (fb_var->green.offset == 5) && + (fb_var->green.length == 6) && + (fb_var->blue.offset == 11) && + (fb_var->blue.length == 5); +} + +static bool is_rgb888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 24) && + (fb_var->red.offset == 16) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 0) && + (fb_var->blue.length == 8); +} + +static bool is_bgr888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 24) && + (fb_var->red.offset == 0) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 16) && + (fb_var->blue.length == 8); +} + +static bool is_xrgb8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 16) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 0) && + (fb_var->blue.length == 8) && + (fb_var->transp.length == 0); +} + +static bool is_xbgr8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 0) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 16) && + (fb_var->blue.length == 8) && + (fb_var->transp.length == 0); +} + +static bool is_rgbx8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 24) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 16) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 8) && + (fb_var->blue.length == 8) && + (fb_var->transp.length == 0); +} + +static bool is_bgrx8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 8) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 16) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 24) && + (fb_var->blue.length == 8) && + (fb_var->transp.length == 0); +} + +static bool is_argb8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 16) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 0) && + (fb_var->blue.length == 8) && + (fb_var->transp.offset == 24) && + (fb_var->transp.length == 8); +} + +static bool is_abgr8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 0) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 8) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 16) && + (fb_var->blue.length == 8) && + (fb_var->transp.offset == 24) && + (fb_var->transp.length == 8); +} + +static bool is_rgba8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 24) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 16) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 8) && + (fb_var->blue.length == 8) && + (fb_var->transp.offset == 0) && + (fb_var->transp.length == 8); +} + +static bool is_bgra8888(const struct fb_var_screeninfo *fb_var) +{ + return (fb_var->bits_per_pixel == 32) && + (fb_var->red.offset == 8) && + (fb_var->red.length == 8) && + (fb_var->green.offset == 16) && + (fb_var->green.length == 8) && + (fb_var->blue.offset == 24) && + (fb_var->blue.length == 8) && + (fb_var->transp.offset == 0) && + (fb_var->transp.length == 8); +} + +struct format_map { + bool (*is_format)(const struct fb_var_screeninfo *fb_var); + uint32_t format; +}; + +/** + * drm_fbconv_format_of_fb_var_screeninfo - Returns a DRM_FORMAT constant + * from an fb_var_screeninfo structure + * @fb_info: the fb_var_screeninfo structure + * Returns: + * A DRM_FORMAT constant on success, or + * DRM_FORMAT_INVALID otherwise. + */ +uint32_t +drm_fbconv_format_of_fb_var_screeninfo(const struct fb_var_screeninfo *fb_var) +{ + static const struct format_map map[] = { + { is_c8, DRM_FORMAT_C8 }, /* 256-color palette */ + { is_rgb565, DRM_FORMAT_RGB565 }, + { is_bgr565, DRM_FORMAT_BGR565 }, + { is_rgb888, DRM_FORMAT_RGB888 }, + { is_bgr888, DRM_FORMAT_BGR888 }, + { is_xrgb8888, DRM_FORMAT_XRGB8888 }, + { is_xbgr8888, DRM_FORMAT_XBGR8888 }, + { is_rgbx8888, DRM_FORMAT_RGBX8888 }, + { is_bgrx8888, DRM_FORMAT_BGRX8888 }, + { is_argb8888, DRM_FORMAT_ARGB8888 }, + { is_abgr8888, DRM_FORMAT_ABGR8888 }, + { is_rgba8888, DRM_FORMAT_RGBA8888 }, + { is_bgra8888, DRM_FORMAT_BGRA8888 } + }; + + size_t i; + + if (fb_var->bits_per_pixel < 8) + goto err; /* at least 8-bit color required */ + if (fb_var->grayscale == 1) + goto err; /* grayscale output is not supported */ + + for (i = 0; i < ARRAY_SIZE(map); ++i) { + if (map[i].is_format(fb_var)) + return map[i].format; + } + +err: + return DRM_FORMAT_INVALID; +} +EXPORT_SYMBOL(drm_fbconv_format_of_fb_var_screeninfo); + +/** + * drm_fbconv_format_of_fb_info - Returns a DRM_FORMAT constant from + * an fb_info structure + * @fb_info: the fb_info structure + * Returns: + * A DRM_FORMAT constant on success, or + * DRM_FORMAT_INVALID otherwise. + */ +uint32_t drm_fbconv_format_of_fb_info(const struct fb_info *fb_info) +{ + if (fb_info->fix.type != FB_TYPE_PACKED_PIXELS) + return DRM_FORMAT_INVALID; /* no multi-plane formats */ + + return drm_fbconv_format_of_fb_var_screeninfo(&fb_info->var); +} +EXPORT_SYMBOL(drm_fbconv_format_of_fb_info); + +static void set_fb_bitfield(struct fb_bitfield *bits, __u32 offset, + __u32 length) +{ + bits->offset = offset; + bits->length = length; + bits->msb_right = 0; +} + +static void set_c8(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 8; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 0, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_rgb565(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 16; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 11, 5); + set_fb_bitfield(&fb_var->green, 5, 6); + set_fb_bitfield(&fb_var->blue, 0, 5); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_bgr565(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 16; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 5); + set_fb_bitfield(&fb_var->green, 5, 6); + set_fb_bitfield(&fb_var->blue, 11, 5); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_rgb888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 24; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_bgr888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 24; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_xrgb8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 24, 0); + fb_var->nonstd = 0; +} + +static void set_xbgr8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 24, 0); + fb_var->nonstd = 0; +} + +static void set_rgbx8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 24, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 8, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_bgrx8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 8, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 24, 8); + set_fb_bitfield(&fb_var->transp, 0, 0); + fb_var->nonstd = 0; +} + +static void set_argb8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 16, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 0, 8); + set_fb_bitfield(&fb_var->transp, 24, 8); + fb_var->nonstd = 0; +} + +static void set_abgr8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 0, 8); + set_fb_bitfield(&fb_var->green, 8, 8); + set_fb_bitfield(&fb_var->blue, 16, 8); + set_fb_bitfield(&fb_var->transp, 24, 8); + fb_var->nonstd = 0; +} + +static void set_rgba8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 24, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 8, 8); + set_fb_bitfield(&fb_var->transp, 0, 8); + fb_var->nonstd = 0; +} + +static void set_bgra8888(struct fb_var_screeninfo *fb_var) +{ + fb_var->bits_per_pixel = 32; + fb_var->grayscale = 0; + set_fb_bitfield(&fb_var->red, 8, 8); + set_fb_bitfield(&fb_var->green, 16, 8); + set_fb_bitfield(&fb_var->blue, 24, 8); + set_fb_bitfield(&fb_var->transp, 0, 8); + fb_var->nonstd = 0; +} + +/** + * drm_fbconv_update_fb_var_screen_info_from_format - sets the pixel format + * of a fb_var_screeninfo structure from a DRM_FORMAT constant + * @fb_var: the fb_var_screeninfo structure to update + * @format: a DRM_FORMAT constant + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_fbconv_update_fb_var_screeninfo_from_format( + struct fb_var_screeninfo *fb_var, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_C8: + set_c8(fb_var); + break; + case DRM_FORMAT_RGB565: + set_rgb565(fb_var); + break; + case DRM_FORMAT_BGR565: + set_bgr565(fb_var); + break; + case DRM_FORMAT_RGB888: + set_rgb888(fb_var); + break; + case DRM_FORMAT_BGR888: + set_bgr888(fb_var); + break; + case DRM_FORMAT_XRGB8888: + set_xrgb8888(fb_var); + break; + case DRM_FORMAT_XBGR8888: + set_xbgr8888(fb_var); + break; + case DRM_FORMAT_RGBX8888: + set_rgbx8888(fb_var); + break; + case DRM_FORMAT_BGRX8888: + set_bgrx8888(fb_var); + break; + case DRM_FORMAT_ARGB8888: + set_argb8888(fb_var); + break; + case DRM_FORMAT_ABGR8888: + set_abgr8888(fb_var); + break; + case DRM_FORMAT_RGBA8888: + set_rgba8888(fb_var); + break; + case DRM_FORMAT_BGRA8888: + set_bgra8888(fb_var); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(drm_fbconv_update_fb_var_screeninfo_from_format); diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h new file mode 100644 index 000000000000..6b2ed12b579a --- /dev/null +++ b/include/drm/drm_fbconv_helper.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef DRM_FBCONV_HELPER_H +#define DRM_FBCONV_HELPER_H + +#include <drm/drm_fourcc.h> + +struct fb_info; +struct fb_var_screeninfo; + +/* + * Format conversion helpers + */ + +uint32_t +drm_fbconv_format_of_fb_var_screeninfo(const struct fb_var_screeninfo *fb_var); + +uint32_t drm_fbconv_format_of_fb_info(const struct fb_info *fb_info); + +int drm_fbconv_update_fb_var_screeninfo_from_format( + struct fb_var_screeninfo *fb_var, uint32_t format); + +#endif
Hi Thomas.
On Mon, Oct 14, 2019 at 04:04:06PM +0200, Thomas Zimmermann wrote:
DRM uses FOURCC constants to describe pixel formats, fbdev uses a per-component bitfield structure. The functions in this patch convert between the two.
A few nits below.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
drivers/gpu/drm/drm_fbconv_helper.c | 435 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 23 ++ 2 files changed, 458 insertions(+) create mode 100644 include/drm/drm_fbconv_helper.h
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index 0cb46d2c98c3..af45358a156a 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -1 +1,436 @@ // SPDX-License-Identifier: GPL-2.0-or-later
+#include <asm/byteorder.h>
+#include <linux/fb.h>
<asm/*> after <linux/*> So we in this way pick the more general include file first.
+struct format_map {
- bool (*is_format)(const struct fb_var_screeninfo *fb_var);
- uint32_t format;
+};
We are in the kernel - where I think u32 is preferred over the longer uint32_t. If I grep in drm/* then they seems be be equally popular, so feel free to ignore this comment.
+static void set_fb_bitfield(struct fb_bitfield *bits, __u32 offset,
__u32 length)
This is not uapi - so u32 is preferred.
Both comments apply to the whole file.
I did not see that this was wired into the kernel-doc in Documentation/ but maybe I just missed it.
With my comments considered you can add: Acked-by: Sam Ravnborg sam@ravnborg.org
All code looks sane, but as I have not grasped the bigger picture this can hardly be a review.
Sam
Hi
Am 14.10.19 um 22:30 schrieb Sam Ravnborg:
Hi Thomas.
On Mon, Oct 14, 2019 at 04:04:06PM +0200, Thomas Zimmermann wrote:
DRM uses FOURCC constants to describe pixel formats, fbdev uses a per-component bitfield structure. The functions in this patch convert between the two.
A few nits below.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
drivers/gpu/drm/drm_fbconv_helper.c | 435 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 23 ++ 2 files changed, 458 insertions(+) create mode 100644 include/drm/drm_fbconv_helper.h
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index 0cb46d2c98c3..af45358a156a 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -1 +1,436 @@ // SPDX-License-Identifier: GPL-2.0-or-later
+#include <asm/byteorder.h>
+#include <linux/fb.h>
<asm/*> after <linux/*> So we in this way pick the more general include file first.
Ok.
+struct format_map {
- bool (*is_format)(const struct fb_var_screeninfo *fb_var);
- uint32_t format;
+};
We are in the kernel - where I think u32 is preferred over the longer uint32_t. If I grep in drm/* then they seems be be equally popular, so feel free to ignore this comment.
I generally use types that are used by related interfaces. Here it's uint32_t because most other places use uint32_t for storing DRM_FORMAT constants.
+static void set_fb_bitfield(struct fb_bitfield *bits, __u32 offset,
__u32 length)
This is not uapi - so u32 is preferred.
Same as above. The __u32 comes from the fb_bitfield structure.
Best regards Thomas
Both comments apply to the whole file.
I did not see that this was wired into the kernel-doc in Documentation/ but maybe I just missed it.
With my comments considered you can add: Acked-by: Sam Ravnborg sam@ravnborg.org
All code looks sane, but as I have not grasped the bigger picture this can hardly be a review.
Sam _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
DRM uses struct drm_display_mode to describe a display mode. The conversion functions fill it from fbdev data strucutures, and vice versa.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 201 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 31 +++++ 2 files changed, 232 insertions(+)
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index af45358a156a..e5a58a361ae9 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -5,6 +5,7 @@ #include <linux/fb.h>
#include <drm/drm_fbconv_helper.h> +#include <drm/drm_modes.h>
/* * Format conversion helpers @@ -434,3 +435,203 @@ int drm_fbconv_update_fb_var_screeninfo_from_format( return 0; } EXPORT_SYMBOL(drm_fbconv_update_fb_var_screeninfo_from_format); + +/* + * Mode conversion helpers + */ + +/** + * drm_mode_update_from_fb_videomode - Sets a drm_display mode struecture + * from an fb_videomode structure + * @mode: the DRM display mode structure to update + * @fb_mode: an fb_videomode structure + */ +void drm_mode_update_from_fb_videomode(struct drm_display_mode *mode, + const struct fb_videomode *fb_mode) +{ + mode->type = DRM_MODE_TYPE_DRIVER; + + mode->clock = PICOS2KHZ(fb_mode->pixclock); + + mode->hdisplay = fb_mode->xres; + mode->hsync_start = mode->hdisplay + fb_mode->right_margin; + mode->hsync_end = mode->hsync_start + fb_mode->hsync_len; + mode->htotal = mode->hsync_end + fb_mode->left_margin; + mode->hskew = 0; + + mode->vdisplay = fb_mode->yres; + mode->vsync_start = mode->vdisplay + fb_mode->lower_margin; + mode->vsync_end = mode->vsync_start + fb_mode->vsync_len; + mode->vtotal = mode->vsync_end + fb_mode->upper_margin; + mode->vscan = 0; + + mode->flags = 0; + + if (fb_mode->sync & FB_SYNC_HOR_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (fb_mode->sync & FB_SYNC_VERT_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + mode->flags |= DRM_MODE_FLAG_NVSYNC; + + if (fb_mode->sync & FB_SYNC_COMP_HIGH_ACT) + mode->flags |= DRM_MODE_FLAG_CSYNC | DRM_MODE_FLAG_PCSYNC; + + if (fb_mode->vmode & FB_VMODE_INTERLACED) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + + if (fb_mode->vmode & FB_VMODE_DOUBLE) + mode->flags |= DRM_MODE_FLAG_DBLSCAN; + + mode->width_mm = 0; + mode->height_mm = 0; + + mode->vrefresh = fb_mode->refresh; + mode->hsync = mode->clock / mode->vtotal; + + /* final step; depends on previous setup */ + if (fb_mode->name) { + strncpy(mode->name, fb_mode->name, sizeof(mode->name) - 1); + mode->name[sizeof(mode->name) - 1] = '\0'; + } else { + drm_mode_set_name(mode); + } +} +EXPORT_SYMBOL(drm_mode_update_from_fb_videomode); + +/** + * drm_mode_update_from_fb_var_screeninfo - Sets a drm_display mode structure + * from an fb_var_screenmode structure + * @mode: the DRM display mode structure to update + * @fb_var: an fb_var_screeninfo structure + */ +void drm_mode_update_from_fb_var_screeninfo( + struct drm_display_mode *mode, const struct fb_var_screeninfo *fb_var) +{ + struct fb_videomode fb_mode; + + fb_var_to_videomode(&fb_mode, fb_var); + drm_mode_update_from_fb_videomode(mode, &fb_mode); +} +EXPORT_SYMBOL(drm_mode_update_from_fb_var_screeninfo); + +/** + * drm_mode_create_from_fb_videomode - Creates a drm_display mode structure + * from an fb_videomode structure + * @dev: the new mode's DRM device + * @fb_mode: an fb_videomode structure + * Returns: + * A newly allocated DRM display mode structure on success, or + * NULL otherwise + */ +struct drm_display_mode * drm_mode_create_from_fb_videomode( + struct drm_device *dev, const struct fb_videomode *fb_mode) +{ + /* cleared to '0' */ + struct drm_display_mode *mode; + + mode = drm_mode_create(dev); + if (!mode) + return NULL; + + drm_mode_update_from_fb_videomode(mode, fb_mode); + + return mode; +} +EXPORT_SYMBOL(drm_mode_create_from_fb_videomode); + +/** + * drm_fbconv_update_fb_videomode_from_mode - updates an fb_videomode + * structure from a DRM display mode + * @fb_mode: the fb_videomode structure to update + * @mode: a DRM display mode + */ +void +drm_fbconv_update_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode) +{ + fb_mode->name = NULL; + fb_mode->refresh = mode->vrefresh; + fb_mode->xres = mode->hdisplay; + fb_mode->yres = mode->vdisplay; + fb_mode->pixclock = KHZ2PICOS(mode->clock); + fb_mode->left_margin = mode->htotal - mode->hsync_end; + fb_mode->right_margin = mode->hsync_start - mode->hdisplay; + fb_mode->upper_margin = mode->vtotal - mode->vsync_end; + fb_mode->lower_margin = mode->vsync_start - mode->vdisplay; + fb_mode->hsync_len = mode->hsync_end - mode->hsync_start; + fb_mode->vsync_len = mode->vsync_end - mode->vsync_start; + + fb_mode->sync = 0; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + fb_mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + fb_mode->sync |= FB_SYNC_VERT_HIGH_ACT; + if (mode->flags & (DRM_MODE_FLAG_CSYNC | DRM_MODE_FLAG_PCSYNC)) + fb_mode->sync |= FB_SYNC_COMP_HIGH_ACT; + + fb_mode->vmode = 0; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + fb_mode->vmode |= FB_VMODE_INTERLACED; + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + fb_mode->vmode |= FB_VMODE_DOUBLE; + + fb_mode->flag = 0; +} +EXPORT_SYMBOL(drm_fbconv_update_fb_videomode_from_mode); + +/** + * drm_fbconv_init_fb_videomode_from_mode - initializes an fb_videomode + * structure from a DRM display mode + * @fb_mode: the fb_videomode structure to update + * @mode: a DRM display mode + * + * This is the same as drm_fbconv_update_fb_videomode_from_mode(), but + * first clears the fb_screeninfo structure to 0. + */ +void drm_fbconv_init_fb_videomode_from_mode( + struct fb_videomode *fb_mode, const struct drm_display_mode *mode) +{ + memset(fb_mode, 0, sizeof(*fb_mode)); + drm_fbconv_update_fb_videomode_from_mode(fb_mode, mode); +} +EXPORT_SYMBOL(drm_fbconv_init_fb_videomode_from_mode); + +/** + * drm_fbconv_update_fb_var_screeninfo_from_mode - updates an + * fb_var_screeninfo structure from a DRM display mode + * @fb_var: the fb_var_screeninfo structure to update + * @mode: a DRM display mode + */ +void drm_fbconv_update_fb_var_screeninfo_from_mode( + struct fb_var_screeninfo *fb_var, const struct drm_display_mode *mode) +{ + struct fb_videomode fb_mode; + + drm_fbconv_init_fb_videomode_from_mode(&fb_mode, mode); + fb_videomode_to_var(fb_var, &fb_mode); + + fb_var->height = mode->height_mm; + fb_var->width = mode->width_mm; +} +EXPORT_SYMBOL(drm_fbconv_update_fb_var_screeninfo_from_mode); + +/** + * drm_fbconv_init_fb_var_screeninfo_from_mode - initialize an + * fb_var_screeninfo structure from a DRM display mode + * @fb_var: the fb_var_screeninfo structure to update + * @mode: a DRM display mode + * + * This is the same as drm_fbconv_update_fb_var_screeninfo_from_mode(), but + * first clears the fb_screeninfo structure to 0. + */ +void drm_fbconv_init_fb_var_screeninfo_from_mode( + struct fb_var_screeninfo *fb_var, const struct drm_display_mode *mode) +{ + memset(fb_var, 0, sizeof(*fb_var)); + drm_fbconv_update_fb_var_screeninfo_from_mode(fb_var, mode); +} +EXPORT_SYMBOL(drm_fbconv_init_fb_var_screeninfo_from_mode); diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h index 6b2ed12b579a..cbb13228c76c 100644 --- a/include/drm/drm_fbconv_helper.h +++ b/include/drm/drm_fbconv_helper.h @@ -5,8 +5,12 @@
#include <drm/drm_fourcc.h>
+struct drm_device; +struct drm_display_mode; struct fb_info; struct fb_var_screeninfo; +struct fb_var_screeninfo; +struct fb_videomode;
/* * Format conversion helpers @@ -20,4 +24,31 @@ uint32_t drm_fbconv_format_of_fb_info(const struct fb_info *fb_info); int drm_fbconv_update_fb_var_screeninfo_from_format( struct fb_var_screeninfo *fb_var, uint32_t format);
+/* + * Mode conversion helpers + */ + +void drm_mode_update_from_fb_videomode(struct drm_display_mode *mode, + const struct fb_videomode *fb_mode); + +void drm_mode_update_from_fb_var_screeninfo( + struct drm_display_mode *mode, const struct fb_var_screeninfo *fb_var); + +struct drm_display_mode * drm_mode_create_from_fb_videomode( + struct drm_device *dev, const struct fb_videomode *fb_mode); + +void +drm_fbconv_update_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode); + +void +drm_fbconv_init_fb_videomode_from_mode(struct fb_videomode *fb_mode, + const struct drm_display_mode *mode); + +void drm_fbconv_update_fb_var_screeninfo_from_mode( + struct fb_var_screeninfo *var, const struct drm_display_mode *mode); + +void drm_fbconv_init_fb_var_screeninfo_from_mode( + struct fb_var_screeninfo *var, const struct drm_display_mode *mode); + #endif
Modesetting for fbconv supports a single display pipeline with CRTC, primary plane, encoder and connector. It's implementation is based on struct drm_simple_display_pipe, which fits this use case nicely.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 382 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 78 ++++++ 2 files changed, 460 insertions(+)
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index e5a58a361ae9..4cda1f15e072 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -4,8 +4,13 @@
#include <linux/fb.h>
+#include <drm/drm_atomic_helper.h> #include <drm/drm_fbconv_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modes.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h>
/* * Format conversion helpers @@ -635,3 +640,380 @@ void drm_fbconv_init_fb_var_screeninfo_from_mode( drm_fbconv_update_fb_var_screeninfo_from_mode(fb_var, mode); } EXPORT_SYMBOL(drm_fbconv_init_fb_var_screeninfo_from_mode); + +/* + * Connector + */ + +static int connector_helper_get_modes(struct drm_connector *connector) +{ + return 0; +} + +static int connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + return connector_status_connected; +} + +static enum drm_mode_status connector_helper_mode_valid( + struct drm_connector *connector, struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static int connector_helper_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + return 0; +} + +static void connector_helper_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *state) +{ } + +static const struct drm_connector_helper_funcs connector_helper_funcs = { + .get_modes = connector_helper_get_modes, + .detect_ctx = connector_helper_detect_ctx, + .mode_valid = connector_helper_mode_valid, + .best_encoder = NULL, /* use default */ + .atomic_best_encoder = NULL, /* use best_encoder instead */ + .atomic_check = connector_helper_atomic_check, + .atomic_commit = connector_helper_atomic_commit +}; + +static enum drm_connector_status connector_detect( + struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void connector_force(struct drm_connector *connector) +{ } + +static void connector_destroy(struct drm_connector *connector) +{ } + +static int connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val) +{ + return -EINVAL; +} + +static int connector_atomic_get_property( + struct drm_connector *connector, + const struct drm_connector_state *state, struct drm_property *property, + uint64_t *val) +{ + return -EINVAL; +} + +static const struct drm_connector_funcs connector_funcs = { + .dpms = NULL, /* not used by atomic drivers */ + .reset = drm_atomic_helper_connector_reset, + .detect = connector_detect, + .force = connector_force, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = NULL, + .late_register = NULL, + .early_unregister = NULL, + .destroy = connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = connector_atomic_set_property, + .atomic_get_property = connector_atomic_get_property, + .atomic_print_state = NULL +}; + +/* + * Simple display pipe + */ + +/** + * drm_fbconv_simple_display_pipe_mode_valid - default implementation for + * struct drm_simple_display_pipe_funcs.mode_valid + * @crtc: the DRM CRTC structure + * @mode: the display mode to validate + * Returns: + * MODE_OK on success, or + * a MODE constant otherwise + */ +enum drm_mode_status +drm_fbconv_simple_display_pipe_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + return MODE_OK; +} +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_valid); + +/** + * drm_fbconv_simple_display_pipe_mode_fixup - default implementation for + * struct drm_simple_display_pipe_funcs.mode_fixup + * @crtc: the DRM CRTC structure + * @mode: the display mode + * @adjusted_mode: the adjusted display mode + * Returns: + * true on success, or + * false otherwise + */ +bool drm_fbconv_simple_display_pipe_mode_fixup( + struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_fixup); + +/** + * drm_fbconv_simple_display_pipe_enable - default implementation for + * struct drm_simple_display_pipe_funcs.enable + * @pipe: the display pipe structure + * @crtc_state: the new CRTC state + * @plane_state: the new plane state + */ +void +drm_fbconv_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ } +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_enable); + +/** + * drm_fbconv_simple_display_pipe_disable - default implementation for + * struct drm_simple_display_pipe_funcs.disable + * @pipe: the display pipe structure + */ +void +drm_fbconv_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) +{ } +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_disable); + +/** + * drm_fbconv_simple_display_pipe_check - default implementation for + * struct drm_simple_display_pipe_funcs.check + * @pipe: the display pipe structure + * @plane_state: the new plane state + * @crtc_state: the new CRTC state + */ +int +drm_fbconv_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state) +{ + return 0; +} +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_check); + +/** + * drm_fbconv_simple_display_pipe_mode_update - default implementation for + * struct drm_simple_display_pipe_funcs.update + * @pipe: the display pipe structure + * @old_plane_state: the old plane state + */ +void +drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_plane_state) +{ } +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_update); + +/** + * drm_fbconv_simple_display_pipe_prepare_fb - default implementation for + * struct drm_simple_display_pipe_funcs.prepare_fb + * @pipe: the display pipe structure + * @plane_state: the new plane state + * Returns: + * 0 on success, or + * a negative error code otherwise. + * + * The implementation of struct drm_simple_display_pipe_funcs.prepare_fb + * maps the framebuffer's buffer object and the fbdev's screen memory, if + * necessary. After converting the fbdev driver to DRM, only the buffer-object + * mapping should remaing. See drm_fbconv_simple_display_pipe_cleanup_fb() for + * the corresponding clean-up helper. + */ +int +drm_fbconv_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ } +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_prepare_fb); + +/** + * drm_fbconv_simple_display_pipe_cleanup_fb - default implementation for + * struct drm_simple_display_pipe_funcs.cleanup_fb + * @pipe: the display pipe structure + * @plane_state: the old plane state + * + * This function cleans up the framebuffer state after a plane update. See + * drm_fbconv_simple_display_pipe_prepare_fb() for the corresponding prepare + * helper. + */ +void +drm_fbconv_simple_display_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ } + +static const struct drm_simple_display_pipe_funcs simple_display_pipe_funcs = { + .mode_valid = drm_fbconv_simple_display_pipe_mode_valid, + .mode_fixup = drm_fbconv_simple_display_pipe_mode_fixup, + .enable = drm_fbconv_simple_display_pipe_enable, + .disable = drm_fbconv_simple_display_pipe_disable, + .check = drm_fbconv_simple_display_pipe_check, + .update = drm_fbconv_simple_display_pipe_update, + .prepare_fb = drm_fbconv_simple_display_pipe_prepare_fb, + .cleanup_fb = drm_fbconv_simple_display_pipe_cleanup_fb, +}; + +/* + * Mode config + */ + +static enum drm_mode_status mode_config_mode_valid( + struct drm_device *dev, const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .get_format_info = NULL, + /* DRM porting notes: the output_poll_changed callback is used by + * fb helpers to implement fbdev emulation. If you're porting an + * fbdev driver to DRM and enable fbdev emulation, this callback + * will become useful. + */ + .output_poll_changed = drm_fb_helper_output_poll_changed, + .mode_valid = mode_config_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .atomic_state_alloc = NULL, + .atomic_state_clear = NULL, + .atomic_state_free = NULL +}; + +/** + * drm_fbconv_modeset_init - initializes an fbconv modesetting structure + * @modeset: the fbconv modesetting structure to initialize + * @dev: the DRM device + * @fb_info: the fbdev driver's fb_info structure + * @max_width: the maximum display width that is supported by + * the device + * @max_height: the maximum display height that is supported by + * the device + * @preferred_depth: the device's preferred color depth + * Returns: + * 0 on success, or + * a negative error code otherwise + * + * This function initializes an instance of struct drm_fbconv_modeset. The + * supplied values for max_width, max_height, and max_depth should match the + * devices capabilities and be supported by the fbdev driver. DRM helpers + * will use these to auto-configure and validate display settings. + */ +int drm_fbconv_modeset_init(struct drm_fbconv_modeset *modeset, + struct drm_device *dev, struct fb_info *fb_info, + unsigned int max_width, unsigned int max_height, + unsigned int preferred_depth) +{ + struct drm_mode_config *mode_config = &dev->mode_config; + + modeset->dev = dev; + modeset->fb_info = fb_info; + + drm_mode_config_init(dev); + + mode_config->max_width = (int)max_width; + mode_config->max_height = (int)max_height; + mode_config->fb_base = fb_info->fix.smem_start; + mode_config->preferred_depth = preferred_depth; + mode_config->prefer_shadow_fbdev = true; + mode_config->funcs = &mode_config_funcs; + + return 0; +} +EXPORT_SYMBOL(drm_fbconv_modeset_init); + +/** + * drm_fbconv_modeset_cleanup - Cleans up an fbconv modesetting structure + * @modeset: the fbconv modesetting structure to clean up + */ +void drm_fbconv_modeset_cleanup(struct drm_fbconv_modeset *modeset) +{ + drm_mode_config_cleanup(modeset->dev); +} +EXPORT_SYMBOL(drm_fbconv_modeset_cleanup); + +/** + * drm_fbconv_modeset_setup_pipe - sets up the display pipeline for fbconv + * @modeset: an fbconv modesetting structure + * @funcs: an implementation of + * struct drm_simple_display_pipe_funcs, or NULL + * @formats: the device's supported display formats + * @format_count: the number of entries in @formats + * @format_modifiers: DRM format modifiers, or NULL + * @connector: the DRM connector, or NULL + * Returns: + * 0 on success, or + * a negative error code otherwise + * + * This function sets up the display pipeline for an initialized instance of + * struct drm_fbconv_modeset. For maximum compatibility with userspace, the + * provided list of formats should contain at least DRM_FORMAT_XRGB8888 and + * DRM_FORMAT_RGB565. The necessary conversion to the hardware's actual + * configuration can be performed by drm_fbconv_simple_display_pipe_update(). + * + * The values for @funcs, @format_modifiers, and @connector should be NULL + * by default. Explicitly settings these parameters will only be helpful for + * refactoring an fbdev driver into a DRM driver. + */ +int +drm_fbconv_modeset_setup_pipe(struct drm_fbconv_modeset *modeset, + const struct drm_simple_display_pipe_funcs *funcs, + const uint32_t *formats, + unsigned int format_count, + const uint64_t *format_modifiers, + struct drm_connector *connector) +{ + int ret; + + /* DRM porting note: Now let's enable the display pipeline. If + * you're porting a framebuffer driver to DRM, you may want to + * set the correct connector type or replace the simple display + * pipeline with something more sophisticated. + */ + + if (!funcs) + funcs = &simple_display_pipe_funcs; + + if (!connector) { + connector = &modeset->connector; + + ret = drm_connector_init(modeset->dev, connector, + &connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ret; + drm_connector_helper_add(connector, &connector_helper_funcs); + + ret = drm_connector_register(connector); + if (ret < 0) + return ret; + + } + + ret = drm_simple_display_pipe_init(modeset->dev, &modeset->pipe, + funcs, formats, format_count, + format_modifiers, connector); + if (ret) + return ret; + + /* Final step: resetting the device's mode config creates + * state for all objects in the mode-setting pipeline. + */ + drm_mode_config_reset(modeset->dev); + + return 0; +} +EXPORT_SYMBOL(drm_fbconv_modeset_setup_pipe); diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h index cbb13228c76c..79716af687c1 100644 --- a/include/drm/drm_fbconv_helper.h +++ b/include/drm/drm_fbconv_helper.h @@ -3,7 +3,11 @@ #ifndef DRM_FBCONV_HELPER_H #define DRM_FBCONV_HELPER_H
+#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> #include <drm/drm_fourcc.h> +#include <drm/drm_simple_kms_helper.h>
struct drm_device; struct drm_display_mode; @@ -51,4 +55,78 @@ void drm_fbconv_update_fb_var_screeninfo_from_mode( void drm_fbconv_init_fb_var_screeninfo_from_mode( struct fb_var_screeninfo *var, const struct drm_display_mode *mode);
+/* + * Simple display pipe + */ + +enum drm_mode_status +drm_fbconv_simple_display_pipe_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode); + +bool drm_fbconv_simple_display_pipe_mode_fixup( + struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +void +drm_fbconv_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state); + +void +drm_fbconv_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe); + +int +drm_fbconv_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state); + +void +drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_plane_state); + +int +drm_fbconv_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + +void +drm_fbconv_simple_display_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + +/* + * Modeset helpers + */ + +/** + * struct drm_fbconv_modeset - contains state for fbconv modesetting + * @connector: the DRM connector + * @pipe: the modesetting pipeline + * @dev: the DRM device + * @fb_info: the fbdev driver's fb_info structure + */ +struct drm_fbconv_modeset { + struct drm_connector connector; + struct drm_simple_display_pipe pipe; + + struct drm_device *dev; + struct fb_info *fb_info; +}; + +static inline struct drm_fbconv_modeset *drm_fbconv_modeset_of_pipe( + struct drm_simple_display_pipe *pipe) +{ + return container_of(pipe, struct drm_fbconv_modeset, pipe); +} + +int drm_fbconv_modeset_init(struct drm_fbconv_modeset *modeset, + struct drm_device *dev, struct fb_info *fb_info, + unsigned int max_width, unsigned int max_height, + unsigned int preferred_depth); +void drm_fbconv_modeset_cleanup(struct drm_fbconv_modeset *modeset); + +int drm_fbconv_modeset_setup_pipe( + struct drm_fbconv_modeset *modeset, + const struct drm_simple_display_pipe_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, struct drm_connector *connector); + #endif
For the update of the primary plane, we copy the content of a SHMEM buffer object to the hardware's on-screen buffer; doing a format conversion if necessary. This is able to support all combinations of framebuffers and hardware, and should work with any fbdev driver.
Occasionally, fbdev drivers require an update of the hardware's gamma tables to not show distorted colors. We also do this during the plane update.
There's no support for horizontal panning, as fbdev drivers vary widely in their capability to do this. Vertical panning is supported to the extend allowed by available video ram. However, this whole functionality is more interesting for porting drivers and not directly required by fbconv itself.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 500 +++++++++++++++++++++++++++- include/drm/drm_fbconv_helper.h | 9 + 3 files changed, 507 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed689201ec81..2ce7749c3157 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -160,6 +160,7 @@ config DRM_DP_CEC config DRM_FBCONV_HELPER tristate "Enable fbdev conversion helpers" depends on DRM + select DRM_GEM_SHMEM_HELPER help Provides helpers for running DRM on top of fbdev drivers. Choose this option if you're converting an fbdev driver to DRM. The diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index 4cda1f15e072..cf218016ac05 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -5,12 +5,17 @@ #include <linux/fb.h>
#include <drm/drm_atomic_helper.h> +#include <drm/drm_damage_helper.h> #include <drm/drm_fbconv_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_format_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_shmem_helper.h> #include <drm/drm_modes.h> #include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> #include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h>
/* * Format conversion helpers @@ -728,10 +733,192 @@ static const struct drm_connector_funcs connector_funcs = { .atomic_print_state = NULL };
+/* + * Colormap updates + */ + +/* provides a default colormap for palette modes */ +static int create_palette_cmap(struct fb_cmap *cmap, + const struct fb_var_screeninfo *fb_var) +{ + __u32 len; + const struct fb_cmap *default_cmap; + int ret; + + len = max3(fb_var->red.length, + fb_var->green.length, + fb_var->blue.length); + if (!len || (len > 31)) { + DRM_ERROR("fbconv: Gamma LUT has invalid bit count of %u\n", + (unsigned int)len); + return -EINVAL; + } + + default_cmap = fb_default_cmap(1ul << len); + if (!default_cmap) { + DRM_ERROR("fbconv: fb_default_cmap() failed\n"); + return -EINVAL; + } + + ret = fb_alloc_cmap(cmap, default_cmap->len, 0); + if (ret) { + DRM_ERROR("fbconv: fb_alloc_cmap() failed: %d\n", ret); + return ret; + } + ret = fb_copy_cmap(default_cmap, cmap); + if (ret) { + DRM_ERROR("fbconv: fb_copy_cmap() failed: %d\n", ret); + goto err_fb_dealloc_cmap; + } + + return 0; + +err_fb_dealloc_cmap: + fb_dealloc_cmap(cmap); + return ret; +} + +/* provides a linear color ramp for RGB modes */ +static int create_linear_cmap(struct fb_cmap *cmap, + const struct fb_var_screeninfo *fb_var) +{ + int ret; + size_t i; + unsigned int j; + u16 *lut; + u16 incr; + u16 *gamma_lut[3]; + __u32 len; + const __u32 gamma_len[3] = { + fb_var->red.length, + fb_var->green.length, + fb_var->blue.length + }; + + len = max3(gamma_len[0], gamma_len[1], gamma_len[2]); + if (!len || (len > 8)) { + DRM_ERROR("fbconv: gamma LUT has invalid bit count of %u\n", + (unsigned int)len); + return -EINVAL; + } + + ret = fb_alloc_cmap(cmap, 1ul << len, 0); + if (ret) { + DRM_ERROR("fbconv: fb_alloc_cmap() failed: %d\n", ret); + return ret; + } + + gamma_lut[0] = cmap->red; + gamma_lut[1] = cmap->green; + gamma_lut[2] = cmap->blue; + + for (i = 0; i < ARRAY_SIZE(gamma_lut); ++i) { + lut = gamma_lut[i]; + len = 1ul << gamma_len[i]; + incr = 0x10000u >> gamma_len[i]; + for (j = 0; j < len; ++j, ++lut) + *lut = incr * j; + + /* In order to have no intensity at index 0 and full + * intensity at the final index of the LUT, we fix-up the + * table's final entries. The fix-up makes intensity grow + * faster near the final entries of the gamma LUT. The human + * eye is more sensitive to changes to the lower intensities, + * so this is probably not directly perceivable. + */ + for (lut -= gamma_len[i], j = gamma_len[i]; j > 0; ++lut) { + --j; + /* subtract 1 to not overflow the LUT's final entry */ + *lut += (incr >> j) - 1; + } + } + + return 0; +} + +static int set_cmap(struct fb_info *fb_info) +{ + struct fb_cmap cmap; + int ret; + + memset(&cmap, 0, sizeof(cmap)); + + switch (fb_info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + ret = create_palette_cmap(&cmap, &fb_info->var); + break; + case FB_VISUAL_DIRECTCOLOR: + ret = create_linear_cmap(&cmap, &fb_info->var); + break; + default: + return 0; + } + if (ret) + return ret; + + ret = fb_set_cmap(&cmap, fb_info); + if (ret) { + DRM_ERROR("fbconv: fb_set_cmap() failed: %d\n", ret); + goto err_fb_dealloc_cmap; + } + fb_dealloc_cmap(&cmap); + + return 0; + +err_fb_dealloc_cmap: + fb_dealloc_cmap(&cmap); + return ret; +} + /* * Simple display pipe */
+static void drm_fbconv_update_fb_var_screeninfo_from_crtc_state( + struct fb_var_screeninfo *fb_var, struct drm_crtc_state *crtc_state) +{ + drm_fbconv_update_fb_var_screeninfo_from_mode( + fb_var, &crtc_state->adjusted_mode); +} + +static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( + struct fb_var_screeninfo *fb_var, struct drm_framebuffer *fb, + size_t vram_size) +{ + unsigned int width, pitch; + uint64_t cpp, lines; + int ret; + + /* Our virtual screen covers all the graphics memory (sans some + * trailing bytes). This allows for setting the scanout buffer's + * address with fb_pan_display(). + */ + + width = fb->pitches[0]; + cpp = fb->format[0].cpp[0]; + do_div(width, cpp); + + if (width > (__u32)-1) + return -EINVAL; /* would overflow fb_var->xres_virtual */ + + pitch = fb->pitches[0]; + lines = vram_size; + do_div(lines, pitch); + + if (lines > (__u32)-1) + return -EINVAL; /* would overflow fb_var->yres_virtual */ + + fb_var->xres_virtual = width; + fb_var->yres_virtual = lines; + + ret = drm_fbconv_update_fb_var_screeninfo_from_format( + fb_var, fb->format[0].format); + if (ret) + return ret; + + return 0; +} + /** * drm_fbconv_simple_display_pipe_mode_valid - default implementation for * struct drm_simple_display_pipe_funcs.mode_valid @@ -767,6 +954,52 @@ bool drm_fbconv_simple_display_pipe_mode_fixup( } EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_fixup);
+/** + * drm_fbconv_blit_rect - copy an area of pixel data from a framebuffer + * to the hardware buffer + * @dst: the on-screen hardware buffer + * @vaddr: the source buffer in kernel address space + * @fb: the framebuffer of the source buffer + * @rect: the area to copy + * Returns: + * 0 on success, or + * a negative error code otherwise. + * + * This function copies the pixel data from a DRM framebuffer to a hardware + * buffer; doing necessary format conversion in the process. Not all + * combinations of source and destination formats are currently supported. + */ +int drm_fbconv_blit_rect(void *dst, void *vaddr, struct drm_framebuffer *fb, + struct drm_rect *rect) +{ + struct drm_device *dev = fb->dev; + + if (!vaddr) + return 0; /* no framebuffer set for plane; no error */ + + if (dev->mode_config.preferred_depth == (fb->format->cpp[0] * 8)) + drm_fb_memcpy_dstclip(dst, vaddr, fb, rect); + + else if (fb->format->cpp[0] == 4 && + dev->mode_config.preferred_depth == 16) + drm_fb_xrgb8888_to_rgb565_dstclip(dst, fb->pitches[0], + vaddr, fb, rect, false); + + else if (fb->format->cpp[0] == 4 && + dev->mode_config.preferred_depth == 24) + drm_fb_xrgb8888_to_rgb888_dstclip(dst, fb->pitches[0], + vaddr, fb, rect); + + else { + /* TODO: add the missing conversion */ + DRM_ERROR("fbconv: mismatching pixel formats\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_fbconv_blit_rect); + /** * drm_fbconv_simple_display_pipe_enable - default implementation for * struct drm_simple_display_pipe_funcs.enable @@ -803,6 +1036,100 @@ drm_fbconv_simple_display_pipe_check(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state, struct drm_crtc_state *crtc_state) { + struct drm_fbconv_modeset *modeset; + struct fb_videomode fb_mode, fb_var_mode; + int ret; + struct fb_var_screeninfo fb_var; + + /* + * CRTC check + */ + + modeset = drm_fbconv_modeset_of_pipe(pipe); + + /* DRM porting notes: when fbcon takes over the console, it regularly + * changes the display mode. Where's apparently no way to detect this + * directly from fbcon itself. DRM's mode information might therefore + * be out of data, after it takes over the display at a later time. + * Here, we test the CRTC's current mode with the fbdev state. If they + * do not match, we request a mode change from DRM. If you port an + * fbdev driver to DRM, you can remove this code section, DRM will + * be in full control of the display device and doesn't have to react + * to changes from external sources. + */ + + if (!crtc_state->mode_changed && crtc_state->adjusted_mode.clock) { + drm_fbconv_init_fb_videomode_from_mode( + &fb_mode, &crtc_state->adjusted_mode); + fb_var_to_videomode(&fb_var_mode, &modeset->fb_info->var); + if (!fb_mode_is_equal(&fb_mode, &fb_var_mode)) + crtc_state->mode_changed = true; + } + + /* TODO: The vblank interrupt is currently not supported. We set + * the corresponding flag as a workaround. Some fbdev drivers + * support FBIO_WAITFORVSYNC, which we might use for querying + * vblanks. + * + * DRM porting notes: if you're porting an fbdev driver to DRM, + * remove this line and instead signal a vblank event from the + * interrupt handler. + */ + crtc_state->no_vblank = true; + + /* + * Plane check + */ + + if (!plane_state->crtc) + return 0; + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + 1 << 16, 1 << 16, + false, true); + if (ret < 0) + return ret; + + if (!plane_state->visible || !plane_state->fb) + return 0; + + /* Virtual screen sizes are not supported. + */ + + if (drm_rect_width(&plane_state->dst) != plane_state->fb->width || + drm_rect_height(&plane_state->dst) != plane_state->fb->height) { + DRM_ERROR("fbconv: virtual screen sizes not supported\n"); + return -EINVAL; + } + if (plane_state->dst.x1 || plane_state->dst.y1) { + DRM_ERROR("fbconv: virtual screen offset not supported\n"); + return -EINVAL; + } + + /* Pixel formats have to be compatible with fbdev. This is + * usually some variation of XRGB. + */ + + if (!pipe->plane.state || + !pipe->plane.state->fb || + pipe->plane.state->fb->format[0].format != + plane_state->fb->format[0].format) { + + if (modeset->fb_info->fbops->fb_check_var) { + memcpy(&fb_var, &modeset->fb_info->var, + sizeof(fb_var)); + drm_fbconv_update_fb_var_screeninfo_from_crtc_state( + &fb_var, crtc_state); + drm_fbconv_update_fb_var_screeninfo_from_framebuffer( + &fb_var, plane_state->fb, + modeset->fb_info->fix.smem_len); + ret = modeset->fb_info->fbops->fb_check_var( + &fb_var, modeset->fb_info); + if (ret < 0) + return ret; + } + } + return 0; } EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_check); @@ -816,7 +1143,119 @@ EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_check); void drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_plane_state) -{ } +{ + struct drm_fbconv_modeset *modeset; + uint32_t format; + struct fb_var_screeninfo fb_var; + int ret; + bool do_blit; + struct drm_rect rect; + struct drm_crtc *crtc = &pipe->crtc; + + /* + * Plane update + */ + + modeset = drm_fbconv_modeset_of_pipe(pipe); + + format = drm_fbconv_format_of_fb_info(modeset->fb_info); + + /* DRM porting notes: Some fbdev drivers report alpha channels for + * their framebuffer, even though they don't support transparent + * primary planes. For the format test below, we ignore the alpha + * channel and use the non-transparent equivalent of the pixel format. + * If you're porting an fbdev driver to DRM, remove this switch + * statement and report the correct format instead. + */ + switch (format) { + case DRM_FORMAT_ARGB8888: + format = DRM_FORMAT_XRGB8888; + break; + case DRM_FORMAT_ABGR8888: + format = DRM_FORMAT_XBGR8888; + break; + case DRM_FORMAT_RGBA8888: + format = DRM_FORMAT_RGBX8888; + break; + case DRM_FORMAT_BGRA8888: + format = DRM_FORMAT_BGRX8888; + break; + default: + break; + } + + if (!pipe->plane.state->fb) { + /* No framebuffer installed; blank display. */ + fb_blank(modeset->fb_info, FB_BLANK_NORMAL); + return; + } + + if ((format != pipe->plane.state->fb->format[0].format) || + (modeset->fb_info->var.xres_virtual != + pipe->plane.state->fb->width)) { + + /* Pixel format changed, update fb_info accordingly + */ + + memcpy(&fb_var, &modeset->fb_info->var, sizeof(fb_var)); + ret = drm_fbconv_update_fb_var_screeninfo_from_framebuffer( + &fb_var, pipe->plane.state->fb, + modeset->fb_info->fix.smem_len); + if (ret) + return; + + fb_var.activate = FB_ACTIVATE_NOW; + + ret = fb_set_var(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + return; + } + } + + if (!old_plane_state->fb || /* first-time update */ + (format != pipe->plane.state->fb->format[0].format)) { + + /* DRM porting notes: Below we set the LUTs for palette and + * gamma correction. This is required by some fbdev drivers, + * such as nvidiafb and atyfb, which don't initialize the + * table to pass-through the framebuffer values unchanged. This + * is actually CRTC state, but the respective function + * crtc_helper_mode_set_nofb() is only called when a CRTC + * property changes, changes in color formats are not handled + * there. When you're porting a fbdev driver to DRM, remove + * the call. Gamma LUTs are CRTC properties and should be + * handled there. Either remove gamma correction or set up + * the respective CRTC properties for userspace. + */ + set_cmap(modeset->fb_info); + } + + memcpy(&fb_var, &modeset->fb_info->var, sizeof(fb_var)); + fb_var.xoffset = 0; + fb_var.yoffset = 0; + + ret = fb_pan_display(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbconv: fb_pan_display() failed: %d\n", ret); + return; + } + + do_blit = drm_atomic_helper_damage_merged(old_plane_state, + pipe->plane.state, + &rect); + if (do_blit) + drm_fbconv_blit_rect(modeset->blit.screen_base, + modeset->blit.vmap, pipe->plane.state->fb, + &rect); + + if (crtc->state->event) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irq(&crtc->dev->event_lock); + } +} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_update);
/** @@ -837,7 +1276,48 @@ EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_update); int drm_fbconv_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) -{ } +{ + struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); + struct fb_info *fb_info = modeset->fb_info; + struct drm_framebuffer *fb = plane_state->fb; + bool unmap_screen_base = false; + void *screen_base; + void *vmap; + int ret; + + if (!fb) + return 0; + + screen_base = fb_info->screen_base; + + if (!screen_base) { + screen_base = ioremap(fb_info->fix.smem_start, + fb_info->fix.smem_len); + if (!screen_base) { + DRM_ERROR("fbconv: ioremap() failed\n"); + return -ENOMEM; + } + unmap_screen_base = true; + } + + vmap = drm_gem_shmem_vmap(fb->obj[0]); + if (!vmap) { + DRM_ERROR("fbconv: drm_gem_shmem_vmap() failed\n"); + ret = -ENOMEM; + goto err_iounmap; + } + + modeset->blit.vmap = vmap; + modeset->blit.screen_base = screen_base; + modeset->blit.unmap_screen_base = unmap_screen_base; + + return 0; + +err_iounmap: + if (unmap_screen_base) + iounmap(screen_base); + return ret; +} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_prepare_fb);
/** @@ -853,7 +1333,21 @@ EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_prepare_fb); void drm_fbconv_simple_display_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) -{ } +{ + struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); + struct drm_framebuffer *fb = plane_state->fb; + + if (!fb) + return; + + drm_gem_shmem_vunmap(fb->obj[0], modeset->blit.vmap); + + if (modeset->blit.unmap_screen_base) + iounmap(modeset->blit.screen_base); + + memset(&modeset->blit, 0, sizeof(modeset->blit)); +} +EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_cleanup_fb);
static const struct drm_simple_display_pipe_funcs simple_display_pipe_funcs = { .mode_valid = drm_fbconv_simple_display_pipe_mode_valid, diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h index 79716af687c1..3e62b5e80af6 100644 --- a/include/drm/drm_fbconv_helper.h +++ b/include/drm/drm_fbconv_helper.h @@ -92,6 +92,9 @@ void drm_fbconv_simple_display_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state);
+int drm_fbconv_blit_rect(void *dst, void *vaddr, struct drm_framebuffer *fb, + struct drm_rect *rect); + /* * Modeset helpers */ @@ -107,6 +110,12 @@ struct drm_fbconv_modeset { struct drm_connector connector; struct drm_simple_display_pipe pipe;
+ struct { + void *vmap; + void *screen_base; + bool unmap_screen_base; + } blit; + struct drm_device *dev; struct fb_info *fb_info; };
Hi Thomas,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Thomas-Zimmermann/DRM-fbconv-helper... config: mips-allmodconfig (attached as .config) compiler: mips-linux-gcc (GCC) 7.4.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.4.0 make.cross ARCH=mips
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All warnings (new ones prefixed by >>):
In file included from arch/mips/include/asm/div64.h:12:0, from include/linux/kernel.h:18, from include/asm-generic/bug.h:19, from arch/mips/include/asm/bug.h:42, from include/linux/bug.h:5, from arch/mips/include/asm/cmpxchg.h:11, from arch/mips/include/asm/atomic.h:22, from include/linux/atomic.h:7, from include/linux/kgdb.h:18, from include/linux/fb.h:5, from drivers/gpu/drm/drm_fbconv_helper.c:5: drivers/gpu/drm/drm_fbconv_helper.c: In function 'drm_fbconv_update_fb_var_screeninfo_from_framebuffer': include/asm-generic/div64.h:226:28: warning: comparison of distinct pointer types lacks a cast (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ ^
drivers/gpu/drm/drm_fbconv_helper.c:899:2: note: in expansion of macro 'do_div'
do_div(width, cpp); ^~~~~~ In file included from include/uapi/linux/swab.h:6:0, from include/linux/swab.h:5, from include/uapi/linux/byteorder/big_endian.h:13, from include/linux/byteorder/big_endian.h:5, from arch/mips/include/uapi/asm/byteorder.h:13, from drivers/gpu/drm/drm_fbconv_helper.c:3: include/asm-generic/div64.h:239:25: warning: right shift count >= width of type [-Wshift-count-overflow] } else if (likely(((n) >> 32) == 0)) { \ ^ include/linux/compiler.h:77:40: note: in definition of macro 'likely' # define likely(x) __builtin_expect(!!(x), 1) ^
drivers/gpu/drm/drm_fbconv_helper.c:899:2: note: in expansion of macro 'do_div'
do_div(width, cpp); ^~~~~~ In file included from arch/mips/include/asm/div64.h:12:0, from include/linux/kernel.h:18, from include/asm-generic/bug.h:19, from arch/mips/include/asm/bug.h:42, from include/linux/bug.h:5, from arch/mips/include/asm/cmpxchg.h:11, from arch/mips/include/asm/atomic.h:22, from include/linux/atomic.h:7, from include/linux/kgdb.h:18, from include/linux/fb.h:5, from drivers/gpu/drm/drm_fbconv_helper.c:5: include/asm-generic/div64.h:243:22: error: passing argument 1 of '__div64_32' from incompatible pointer type [-Werror=incompatible-pointer-types] __rem = __div64_32(&(n), __base); \ ^
drivers/gpu/drm/drm_fbconv_helper.c:899:2: note: in expansion of macro 'do_div'
do_div(width, cpp); ^~~~~~ include/asm-generic/div64.h:217:17: note: expected 'uint64_t * {aka long long unsigned int *}' but argument is of type 'unsigned int *' extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); ^~~~~~~~~~ cc1: some warnings being treated as errors
vim +/do_div +899 drivers/gpu/drm/drm_fbconv_helper.c
883 884 static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( 885 struct fb_var_screeninfo *fb_var, struct drm_framebuffer *fb, 886 size_t vram_size) 887 { 888 unsigned int width, pitch; 889 uint64_t cpp, lines; 890 int ret; 891 892 /* Our virtual screen covers all the graphics memory (sans some 893 * trailing bytes). This allows for setting the scanout buffer's 894 * address with fb_pan_display(). 895 */ 896 897 width = fb->pitches[0]; 898 cpp = fb->format[0].cpp[0];
899 do_div(width, cpp);
900 901 if (width > (__u32)-1) 902 return -EINVAL; /* would overflow fb_var->xres_virtual */ 903 904 pitch = fb->pitches[0]; 905 lines = vram_size; 906 do_div(lines, pitch); 907 908 if (lines > (__u32)-1) 909 return -EINVAL; /* would overflow fb_var->yres_virtual */ 910 911 fb_var->xres_virtual = width; 912 fb_var->yres_virtual = lines; 913 914 ret = drm_fbconv_update_fb_var_screeninfo_from_format( 915 fb_var, fb->format[0].format); 916 if (ret) 917 return ret; 918 919 return 0; 920 } 921
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Thomas,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Thomas-Zimmermann/DRM-fbconv-helper... reproduce: # apt-get install sparse # sparse version: v0.6.1-dirty make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
sparse warnings: (new ones prefixed by >>)
drivers/gpu/drm/drm_fbconv_helper.c:981:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] asn:2 *dst @@ got n:2> *dst @@ drivers/gpu/drm/drm_fbconv_helper.c:981:39: sparse: expected void [noderef] asn:2 *dst drivers/gpu/drm/drm_fbconv_helper.c:981:39: sparse: got void *dst
drivers/gpu/drm/drm_fbconv_helper.c:985:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] asn:2 *dst @@ got n:2> *dst @@ drivers/gpu/drm/drm_fbconv_helper.c:985:51: sparse: expected void [noderef] asn:2 *dst drivers/gpu/drm/drm_fbconv_helper.c:985:51: sparse: got void *dst drivers/gpu/drm/drm_fbconv_helper.c:990:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] asn:2 *dst @@ got n:2> *dst @@ drivers/gpu/drm/drm_fbconv_helper.c:990:51: sparse: expected void [noderef] asn:2 *dst drivers/gpu/drm/drm_fbconv_helper.c:990:51: sparse: got void *dst
drivers/gpu/drm/drm_fbconv_helper.c:1291:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void *screen_base @@ got char [noderef] <asvoid *screen_base @@ drivers/gpu/drm/drm_fbconv_helper.c:1291:21: sparse: expected void *screen_base drivers/gpu/drm/drm_fbconv_helper.c:1291:21: sparse: got char [noderef] asn:2 *screen_base drivers/gpu/drm/drm_fbconv_helper.c:1294:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected void *screen_base @@ got void [noderef] <asvoid *screen_base @@
drivers/gpu/drm/drm_fbconv_helper.c:1294:29: sparse: expected void *screen_base
drivers/gpu/drm/drm_fbconv_helper.c:1294:29: sparse: got void [noderef] asn:2 * drivers/gpu/drm/drm_fbconv_helper.c:1318:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void volatile [noderef] asn:2 *addr @@ got n:2> *addr @@ drivers/gpu/drm/drm_fbconv_helper.c:1318:25: sparse: expected void volatile [noderef] asn:2 *addr drivers/gpu/drm/drm_fbconv_helper.c:1318:25: sparse: got void *screen_base
drivers/gpu/drm/drm_fbconv_helper.c:1346:38: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void volatile [noderef] asn:2 *addr @@ got n:2> *addr @@ drivers/gpu/drm/drm_fbconv_helper.c:1346:38: sparse: expected void volatile [noderef] asn:2 *addr drivers/gpu/drm/drm_fbconv_helper.c:1346:38: sparse: got void *screen_base
vim +981 drivers/gpu/drm/drm_fbconv_helper.c
956 957 /** 958 * drm_fbconv_blit_rect - copy an area of pixel data from a framebuffer 959 * to the hardware buffer 960 * @dst: the on-screen hardware buffer 961 * @vaddr: the source buffer in kernel address space 962 * @fb: the framebuffer of the source buffer 963 * @rect: the area to copy 964 * Returns: 965 * 0 on success, or 966 * a negative error code otherwise. 967 * 968 * This function copies the pixel data from a DRM framebuffer to a hardware 969 * buffer; doing necessary format conversion in the process. Not all 970 * combinations of source and destination formats are currently supported. 971 */ 972 int drm_fbconv_blit_rect(void *dst, void *vaddr, struct drm_framebuffer *fb, 973 struct drm_rect *rect) 974 { 975 struct drm_device *dev = fb->dev; 976 977 if (!vaddr) 978 return 0; /* no framebuffer set for plane; no error */ 979 980 if (dev->mode_config.preferred_depth == (fb->format->cpp[0] * 8))
981 drm_fb_memcpy_dstclip(dst, vaddr, fb, rect);
982 983 else if (fb->format->cpp[0] == 4 && 984 dev->mode_config.preferred_depth == 16)
985 drm_fb_xrgb8888_to_rgb565_dstclip(dst, fb->pitches[0],
986 vaddr, fb, rect, false); 987 988 else if (fb->format->cpp[0] == 4 && 989 dev->mode_config.preferred_depth == 24)
990 drm_fb_xrgb8888_to_rgb888_dstclip(dst, fb->pitches[0],
991 vaddr, fb, rect); 992 993 else { 994 /* TODO: add the missing conversion */ 995 DRM_ERROR("fbconv: mismatching pixel formats\n"); 996 return -EINVAL; 997 } 998 999 return 0; 1000 } 1001 EXPORT_SYMBOL(drm_fbconv_blit_rect); 1002
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
The display mode is set by converting the DRM display mode to an fb_info state and handling it to the fbdev driver's fb_setvar() function. This also requires a color depth, which we take from the value of struct drm_mode_config.preferred_depth
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 113 +++++++++++++++++++++++++++- include/drm/drm_fbconv_helper.h | 2 + 2 files changed, 113 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index cf218016ac05..ca8b43c91266 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -919,6 +919,24 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( return 0; }
+static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe( + struct fb_var_screeninfo *fb_var, struct drm_simple_display_pipe *pipe) +{ + struct drm_plane *primary = pipe->crtc.primary; + struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); + + if (primary && primary->state && primary->state->fb) + return drm_fbconv_update_fb_var_screeninfo_from_framebuffer( + fb_var, primary->state->fb, + modeset->fb_info->fix.smem_len); + + fb_var->xres_virtual = fb_var->xres; + fb_var->yres_virtual = fb_var->yres; + fb_var->bits_per_pixel = modeset->dev->mode_config.preferred_depth; + + return 0; +} + /** * drm_fbconv_simple_display_pipe_mode_valid - default implementation for * struct drm_simple_display_pipe_funcs.mode_valid @@ -950,6 +968,28 @@ bool drm_fbconv_simple_display_pipe_mode_fixup( struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_simple_display_pipe *pipe = + container_of(crtc, struct drm_simple_display_pipe, crtc); + struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); + struct fb_var_screeninfo fb_var; + int ret; + + if (!modeset->fb_info->fbops->fb_check_var) + return true; + + drm_fbconv_init_fb_var_screeninfo_from_mode(&fb_var, mode); + + ret = drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe( + &fb_var, &modeset->pipe); + if (ret) + return true; + + ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info); + if (ret < 0) + return false; + + drm_mode_update_from_fb_var_screeninfo(adjusted_mode, &fb_var); + return true; } EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_fixup); @@ -1000,6 +1040,32 @@ int drm_fbconv_blit_rect(void *dst, void *vaddr, struct drm_framebuffer *fb, } EXPORT_SYMBOL(drm_fbconv_blit_rect);
+/** + * drm_fbconv_blit_fullscreen - copy all pixel data from a framebuffer + * to the hardware buffer + * @dst: the on-screen hardware buffer + * @vaddr: the source buffer in kernel address space + * @fb: the framebuffer of the source buffer + * Returns: + * 0 on success, or + * a negative error code otherwise. + * + * This function is equivalent to drm_fbconv_blit_rect(), but copies the + * framebuffer's complete content. + */ +int drm_fbconv_blit_fullscreen(void *dst, void *vaddr, + struct drm_framebuffer *fb) +{ + struct drm_rect fullscreen = { + .x1 = 0, + .x2 = fb->width, + .y1 = 0, + .y2 = fb->height, + }; + return drm_fbconv_blit_rect(dst, vaddr, fb, &fullscreen); +} +EXPORT_SYMBOL(drm_fbconv_blit_fullscreen); + /** * drm_fbconv_simple_display_pipe_enable - default implementation for * struct drm_simple_display_pipe_funcs.enable @@ -1011,7 +1077,46 @@ void drm_fbconv_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) -{ } +{ + struct drm_fbconv_modeset *modeset; + struct fb_var_screeninfo fb_var; + int ret; + + /* As this is atomic mode setting, any function call is not + * allowed to fail. If it does, an additional test should be + * added to simple_display_pipe_check(). + */ + + modeset = drm_fbconv_modeset_of_pipe(pipe); + + drm_fbconv_init_fb_var_screeninfo_from_mode( + &fb_var, &crtc_state->adjusted_mode); + + if (plane_state && plane_state->fb) { + ret = drm_fbconv_update_fb_var_screeninfo_from_framebuffer( + &fb_var, plane_state->fb, + modeset->fb_info->fix.smem_len); + if (ret) + return; + } else { + fb_var.xres_virtual = fb_var.xres; + fb_var.yres_virtual = fb_var.yres; + } + + fb_var.activate = FB_ACTIVATE_NOW; + + ret = fb_set_var(modeset->fb_info, &fb_var); + if (ret) { + DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + return; + } + + fb_blank(modeset->fb_info, FB_BLANK_UNBLANK); + + drm_fbconv_blit_fullscreen(modeset->blit.screen_base, + modeset->blit.vmap, + plane_state->fb); +} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_enable);
/** @@ -1021,7 +1126,11 @@ EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_enable); */ void drm_fbconv_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) -{ } +{ + struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe); + + fb_blank(modeset->fb_info, FB_BLANK_POWERDOWN); +} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_disable);
/** diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h index 3e62b5e80af6..c7d211f40462 100644 --- a/include/drm/drm_fbconv_helper.h +++ b/include/drm/drm_fbconv_helper.h @@ -94,6 +94,8 @@ drm_fbconv_simple_display_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe,
int drm_fbconv_blit_rect(void *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *rect); +int drm_fbconv_blit_fullscreen(void *dst, void *vaddr, + struct drm_framebuffer *fb);
/* * Modeset helpers
Hi Thomas,
On Mon, Oct 14, 2019 at 4:05 PM Thomas Zimmermann tzimmermann@suse.de wrote:
The display mode is set by converting the DRM display mode to an fb_info state and handling it to the fbdev driver's fb_setvar() function. This also requires a color depth, which we take from the value of struct drm_mode_config.preferred_depth
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
--- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -919,6 +919,24 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( return 0; }
+static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
struct fb_var_screeninfo *fb_var, struct drm_simple_display_pipe *pipe)
+{
struct drm_plane *primary = pipe->crtc.primary;
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
if (primary && primary->state && primary->state->fb)
return drm_fbconv_update_fb_var_screeninfo_from_framebuffer(
fb_var, primary->state->fb,
modeset->fb_info->fix.smem_len);
fb_var->xres_virtual = fb_var->xres;
fb_var->yres_virtual = fb_var->yres;
fb_var->bits_per_pixel = modeset->dev->mode_config.preferred_depth;
This looks wrong to me: IMHO bits_per_pixel should be derived from the fourcc format of the _new_ mode to be set...
return 0;
+}
/**
- drm_fbconv_simple_display_pipe_mode_valid - default implementation for
struct drm_simple_display_pipe_funcs.mode_valid
@@ -950,6 +968,28 @@ bool drm_fbconv_simple_display_pipe_mode_fixup( struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
struct drm_simple_display_pipe *pipe =
container_of(crtc, struct drm_simple_display_pipe, crtc);
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
struct fb_var_screeninfo fb_var;
int ret;
if (!modeset->fb_info->fbops->fb_check_var)
return true;
drm_fbconv_init_fb_var_screeninfo_from_mode(&fb_var, mode);
ret = drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
&fb_var, &modeset->pipe);
if (ret)
return true;
ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info);
... hence this fails if the requested mode is valid with the new fourcc format, but invalid with the old (but preferred) depth. E.g. due to bandwidth limitations, a high-resolution mode is valid with a low color depth, while a high color depth is limited to lower resolutions.
Unfortunately we do not know the new fourcc format here, as both drm_simple_display_pipe_funcs.mode_{valid,fixup}() are passed the mode (from drm_mode_set.mode), but not the new format (from drm_mode_set.fb->format).
Am I missing something? Is the new format available in some other way? Thanks!
if (ret < 0)
return false;
drm_mode_update_from_fb_var_screeninfo(adjusted_mode, &fb_var);
return true;
} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_fixup);
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi Geert,
first of all, thanks for looking at the patch.
Am 28.05.22 um 22:17 schrieb Geert Uytterhoeven:
Hi Thomas,
On Mon, Oct 14, 2019 at 4:05 PM Thomas Zimmermann tzimmermann@suse.de wrote:
The display mode is set by converting the DRM display mode to an fb_info state and handling it to the fbdev driver's fb_setvar() function. This also requires a color depth, which we take from the value of struct drm_mode_config.preferred_depth
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
--- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -919,6 +919,24 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( return 0; }
+static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
struct fb_var_screeninfo *fb_var, struct drm_simple_display_pipe *pipe)
+{
struct drm_plane *primary = pipe->crtc.primary;
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
if (primary && primary->state && primary->state->fb)
return drm_fbconv_update_fb_var_screeninfo_from_framebuffer(
fb_var, primary->state->fb,
modeset->fb_info->fix.smem_len);
fb_var->xres_virtual = fb_var->xres;
fb_var->yres_virtual = fb_var->yres;
fb_var->bits_per_pixel = modeset->dev->mode_config.preferred_depth;
This looks wrong to me: IMHO bits_per_pixel should be derived from the fourcc format of the _new_ mode to be set...
Indeed, this appears to be wrong.
return 0;
+}
- /**
- drm_fbconv_simple_display_pipe_mode_valid - default implementation for
struct drm_simple_display_pipe_funcs.mode_valid
@@ -950,6 +968,28 @@ bool drm_fbconv_simple_display_pipe_mode_fixup( struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
struct drm_simple_display_pipe *pipe =
container_of(crtc, struct drm_simple_display_pipe, crtc);
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
struct fb_var_screeninfo fb_var;
int ret;
if (!modeset->fb_info->fbops->fb_check_var)
return true;
drm_fbconv_init_fb_var_screeninfo_from_mode(&fb_var, mode);
ret = drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
&fb_var, &modeset->pipe);
if (ret)
return true;
ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info);
... hence this fails if the requested mode is valid with the new fourcc format, but invalid with the old (but preferred) depth. E.g. due to bandwidth limitations, a high-resolution mode is valid with a low color depth, while a high color depth is limited to lower resolutions.
I tested the helpers with various fbdev drivers and modified them until all of these drivers produced at least some output. I'm not surprised that there are still bugs.
Unfortunately we do not know the new fourcc format here, as both drm_simple_display_pipe_funcs.mode_{valid,fixup}() are passed the mode (from drm_mode_set.mode), but not the new format (from drm_mode_set.fb->format).
Am I missing something? Is the new format available in some other way?
We can always get the format from the new plane state of modeset->pipe->plane. We'd have this in the atomic_check call. And it appears that drm_fbconv_simple_display_pipe_check() is a better place for this code anyway.
Best regards Thomas
Thanks!
if (ret < 0)
return false;
drm_mode_update_from_fb_var_screeninfo(adjusted_mode, &fb_var);
} EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_mode_fixup);return true;
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi Thomas,
On Mon, May 30, 2022 at 9:47 AM Thomas Zimmermann tzimmermann@suse.de wrote:
first of all, thanks for looking at the patch.
Thank you, your patch series helped a lot.
Am 28.05.22 um 22:17 schrieb Geert Uytterhoeven:
On Mon, Oct 14, 2019 at 4:05 PM Thomas Zimmermann tzimmermann@suse.de wrote:
The display mode is set by converting the DRM display mode to an fb_info state and handling it to the fbdev driver's fb_setvar() function. This also requires a color depth, which we take from the value of struct drm_mode_config.preferred_depth
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
--- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -919,6 +919,24 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer( return 0; }
+static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
struct fb_var_screeninfo *fb_var, struct drm_simple_display_pipe *pipe)
+{
struct drm_plane *primary = pipe->crtc.primary;
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
if (primary && primary->state && primary->state->fb)
return drm_fbconv_update_fb_var_screeninfo_from_framebuffer(
fb_var, primary->state->fb,
modeset->fb_info->fix.smem_len);
fb_var->xres_virtual = fb_var->xres;
fb_var->yres_virtual = fb_var->yres;
fb_var->bits_per_pixel = modeset->dev->mode_config.preferred_depth;
This looks wrong to me: IMHO bits_per_pixel should be derived from the fourcc format of the _new_ mode to be set...
Indeed, this appears to be wrong.
OK.
return 0;
+}
- /**
- drm_fbconv_simple_display_pipe_mode_valid - default implementation for
struct drm_simple_display_pipe_funcs.mode_valid
@@ -950,6 +968,28 @@ bool drm_fbconv_simple_display_pipe_mode_fixup( struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) {
struct drm_simple_display_pipe *pipe =
container_of(crtc, struct drm_simple_display_pipe, crtc);
struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
struct fb_var_screeninfo fb_var;
int ret;
if (!modeset->fb_info->fbops->fb_check_var)
return true;
drm_fbconv_init_fb_var_screeninfo_from_mode(&fb_var, mode);
ret = drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe(
&fb_var, &modeset->pipe);
if (ret)
return true;
ret = modeset->fb_info->fbops->fb_check_var(&fb_var, modeset->fb_info);
... hence this fails if the requested mode is valid with the new fourcc format, but invalid with the old (but preferred) depth. E.g. due to bandwidth limitations, a high-resolution mode is valid with a low color depth, while a high color depth is limited to lower resolutions.
I tested the helpers with various fbdev drivers and modified them until all of these drivers produced at least some output. I'm not surprised that there are still bugs.
As usual, the devil is in the details ;-)
The other issue I was facing are the non-rounding KHZ2PICOS() and PICOS2KHZ() macros, and the numerous back-and-forth conversions: a valid pixclock in kHz is converted to a valid pixclock in ps, and accepted. The returned pixclock in ps is slightly different, and converted to an invalid pixclock in kHz, hence rejected in the next iteration (remember: fb_ops.fb_check_var() should only round up values to match, never round down)...
Unfortunately we do not know the new fourcc format here, as both drm_simple_display_pipe_funcs.mode_{valid,fixup}() are passed the mode (from drm_mode_set.mode), but not the new format (from drm_mode_set.fb->format).
Am I missing something? Is the new format available in some other way?
We can always get the format from the new plane state of modeset->pipe->plane. We'd have this in the atomic_check call. And it appears that drm_fbconv_simple_display_pipe_check() is a better place for this code anyway.
Thanks, I'll give that a try!
Anyway, I finally made some progress with KMS-style mode-setting inside my ataridrm driver. Before, I relied solely on initial fbdev-style mode-setting in the probe function.
Gr{oetje,eeting}s,
Geert
-- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
This patch reimplements fb_blank(), fb_pan_display(), fb_set_cmap() and fb_set_var() for fbconv helpers. The goal is to have all calls to driver callback functions located within fbconv and to reduce the amount of contained work to a minimum.
Some noteable differences to fbdev include:
* Code related to fbcon has been left out. Console support is emulated by DRM and the drivers don't interact directly with it.
* No events are sent out. As the fbconv helpers are not part of the fbdev framework, there are no event listeners anyway.
* Code related to ioctl and user-space has been left out as well. User-space interfaces are provided by DRM.
* Error messages have been added.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 240 +++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index ca8b43c91266..f7f247e30a3d 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -737,6 +737,55 @@ static const struct drm_connector_funcs connector_funcs = { * Colormap updates */
+static int drm_fbconv_set_cmap(struct fb_cmap *cmap, struct fb_info *fb_info) +{ + int i, start, res; + u16 *red, *green, *blue, *transp; + u_int hred, hgreen, hblue, htransp = 0xffff; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + start = cmap->start; + + if (start < 0 || (!fb_info->fbops->fb_setcolreg && + !fb_info->fbops->fb_setcmap)) { + DRM_ERROR("fbconv: Palette not supported.\n"); + return -EINVAL; + } + + if (fb_info->fbops->fb_setcmap) { + res = fb_info->fbops->fb_setcmap(cmap, fb_info); + if (res) { + DRM_ERROR("fbconv: fbops->fb_setcmap() failed: %d\n", + res); + return res; + } + } else { + for (i = 0; i < cmap->len; i++) { + hred = *red++; + hgreen = *green++; + hblue = *blue++; + if (transp) + htransp = *transp++; + res = fb_info->fbops->fb_setcolreg(start++, + hred, hgreen, hblue, + htransp, fb_info); + if (res) { + DRM_ERROR("fbconv: fbops->fb_setcolreg() failed: %d\n", + res); + /* cmap handling is a mess; don't err here */ + break; + } + } + } + + fb_copy_cmap(cmap, &fb_info->cmap); + + return 0; +} + /* provides a default colormap for palette modes */ static int create_palette_cmap(struct fb_cmap *cmap, const struct fb_var_screeninfo *fb_var) @@ -856,11 +905,9 @@ static int set_cmap(struct fb_info *fb_info) if (ret) return ret;
- ret = fb_set_cmap(&cmap, fb_info); - if (ret) { - DRM_ERROR("fbconv: fb_set_cmap() failed: %d\n", ret); + ret = drm_fbconv_set_cmap(&cmap, fb_info); + if (ret) goto err_fb_dealloc_cmap; - } fb_dealloc_cmap(&cmap);
return 0; @@ -891,7 +938,7 @@ static int drm_fbconv_update_fb_var_screeninfo_from_framebuffer(
/* Our virtual screen covers all the graphics memory (sans some * trailing bytes). This allows for setting the scanout buffer's - * address with fb_pan_display(). + * address with drm_fbconv_pan_display(). */
width = fb->pitches[0]; @@ -937,6 +984,165 @@ static int drm_fbconv_update_fb_var_screeninfo_from_simple_display_pipe( return 0; }
+static int drm_fbconv_blank(struct fb_info *fb_info, int blank) +{ + int ret = -EINVAL; + + if (fb_info->fbops->fb_blank) { + ret = fb_info->fbops->fb_blank(blank, fb_info); + if (ret) { + DRM_ERROR("fbconv: fbops->fb_blank() failed: %d\n", + ret); + } + } + return ret; +} + +static int drm_fbconv_pan_display(struct fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + struct fb_fix_screeninfo *fix = &fb_info->fix; + unsigned int yres = fb_info->var.yres; + int err; + + if (var->yoffset > 0) { + if (var->vmode & FB_VMODE_YWRAP) { + if (!fix->ywrapstep || + (var->yoffset % fix->ywrapstep)) { + DRM_ERROR("fbconv: Invalid fix->ywrapstep: %d\n", + fix->ywrapstep); + return -EINVAL; + } + yres = 0; + } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep)) { + DRM_ERROR("fbconv: Invalid fix->ypanstep: %d\n", + fix->ypanstep); + return -EINVAL; + } + } + + if (var->xoffset > 0) { + if (!fix->xpanstep || (var->xoffset % fix->xpanstep)) { + DRM_ERROR("fbconv: Invalid fix->xpanstep: %d\n", + fix->xpanstep); + return -EINVAL; + } + } + + if (!fb_info->fbops->fb_pan_display || + var->yoffset > fb_info->var.yres_virtual - yres || + var->xoffset > fb_info->var.xres_virtual - fb_info->var.xres) { + DRM_ERROR("fbconv: Display panning unsupported\n"); + return -EINVAL; + } + + err = fb_info->fbops->fb_pan_display(var, fb_info); + if (err) { + DRM_ERROR("fbconv: fbops->pan_display() failed: %d", err); + return err; + } + + fb_info->var.xoffset = var->xoffset; + fb_info->var.yoffset = var->yoffset; + + if (var->vmode & FB_VMODE_YWRAP) + fb_info->var.vmode |= FB_VMODE_YWRAP; + else + fb_info->var.vmode &= ~FB_VMODE_YWRAP; + + return 0; +} + +static int drm_fbconv_set_var(struct fb_info *fb_info, + struct fb_var_screeninfo *var) +{ + int ret = 0; + u32 activate; + struct fb_var_screeninfo old_var; + struct fb_videomode mode; + + if (var->activate & FB_ACTIVATE_INV_MODE) { + struct fb_videomode mode1, mode2; + + fb_var_to_videomode(&mode1, var); + fb_var_to_videomode(&mode2, &fb_info->var); + /* make sure we don't delete the videomode of current var */ + ret = fb_mode_is_equal(&mode1, &mode2); + if (ret) { + DRM_ERROR("fbconv: fb_mode_is_equal() failed: %d\n", + ret); + return -EINVAL; + } + + fb_delete_videomode(&mode1, &fb_info->modelist); + + return 0; + } + + if (!(var->activate & FB_ACTIVATE_FORCE) && + !memcmp(&fb_info->var, var, sizeof(*var))) + return 0; + + activate = var->activate; + + /* When using FOURCC mode, make sure the red, green, blue and + * transp fields are set to 0. + */ + if ((fb_info->fix.capabilities & FB_CAP_FOURCC) && var->grayscale > 1) { + if (var->red.offset || var->green.offset || + var->blue.offset || var->transp.offset || + var->red.length || var->green.length || + var->blue.length || var->transp.length || + var->red.msb_right || var->green.msb_right || + var->blue.msb_right || var->transp.msb_right) { + DRM_ERROR("fbconv: Invalid color offsets in FOURCC mode\n"); + return -EINVAL; + } + } + + if (!fb_info->fbops->fb_check_var) { + *var = fb_info->var; + return 0; + } + + ret = fb_info->fbops->fb_check_var(var, fb_info); + if (ret) { + DRM_ERROR("fbconv: fbops->fb_check_var() failed: %d\n", ret); + return ret; + } + + if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) + return 0; + + old_var = fb_info->var; + fb_info->var = *var; + + if (fb_info->fbops->fb_set_par) { + ret = fb_info->fbops->fb_set_par(fb_info); + if (ret) { + fb_info->var = old_var; + DRM_ERROR("fbconv: fbops->fb_set_par() failed: %d\n", + ret); + return ret; + } + } + + drm_fbconv_pan_display(fb_info, &fb_info->var); + drm_fbconv_set_cmap(&fb_info->cmap, fb_info); + fb_var_to_videomode(&mode, &fb_info->var); + + if (fb_info->modelist.prev && fb_info->modelist.next && + !list_empty(&fb_info->modelist)) + ret = fb_add_videomode(&mode, &fb_info->modelist); + + if (ret) { + DRM_ERROR("fbconv: fb_add_videomode() failed: %d\n", ret); + return ret; + } + + return 0; +} + /** * drm_fbconv_simple_display_pipe_mode_valid - default implementation for * struct drm_simple_display_pipe_funcs.mode_valid @@ -1105,13 +1311,11 @@ drm_fbconv_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
fb_var.activate = FB_ACTIVATE_NOW;
- ret = fb_set_var(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + ret = drm_fbconv_set_var(modeset->fb_info, &fb_var); + if (ret) return; - }
- fb_blank(modeset->fb_info, FB_BLANK_UNBLANK); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_UNBLANK);
drm_fbconv_blit_fullscreen(modeset->blit.screen_base, modeset->blit.vmap, @@ -1129,7 +1333,7 @@ drm_fbconv_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe) { struct drm_fbconv_modeset *modeset = drm_fbconv_modeset_of_pipe(pipe);
- fb_blank(modeset->fb_info, FB_BLANK_POWERDOWN); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_POWERDOWN); } EXPORT_SYMBOL(drm_fbconv_simple_display_pipe_disable);
@@ -1295,7 +1499,7 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
if (!pipe->plane.state->fb) { /* No framebuffer installed; blank display. */ - fb_blank(modeset->fb_info, FB_BLANK_NORMAL); + drm_fbconv_blank(modeset->fb_info, FB_BLANK_NORMAL); return; }
@@ -1315,11 +1519,9 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
fb_var.activate = FB_ACTIVATE_NOW;
- ret = fb_set_var(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_set_var() failed: %d\n", ret); + ret = drm_fbconv_set_var(modeset->fb_info, &fb_var); + if (ret) return; - } }
if (!old_plane_state->fb || /* first-time update */ @@ -1344,11 +1546,9 @@ drm_fbconv_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, fb_var.xoffset = 0; fb_var.yoffset = 0;
- ret = fb_pan_display(modeset->fb_info, &fb_var); - if (ret) { - DRM_ERROR("fbconv: fb_pan_display() failed: %d\n", ret); + ret = drm_fbconv_pan_display(modeset->fb_info, &fb_var); + if (ret) return; - }
do_blit = drm_atomic_helper_damage_merged(old_plane_state, pipe->plane.state,
The implementation of drm_fbconv_fill_fb_info() sets up an fbdev driver's fb_info structure for use. It's similar to register_framebuffer(), but does not create device files, register the driver with the fbdev code or create a console. drm_fbconv_cleanup_fb_info() does the inverse.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/gpu/drm/drm_fbconv_helper.c | 123 ++++++++++++++++++++++++++++ include/drm/drm_fbconv_helper.h | 7 ++ 2 files changed, 130 insertions(+)
diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index f7f247e30a3d..7d7e4da2a29e 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -3,6 +3,7 @@ #include <asm/byteorder.h>
#include <linux/fb.h> +#include <linux/major.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_damage_helper.h> @@ -1722,6 +1723,9 @@ int drm_fbconv_modeset_init(struct drm_fbconv_modeset *modeset, { struct drm_mode_config *mode_config = &dev->mode_config;
+ if (WARN_ON(fb_info->node != FB_MAX)) + return -EINVAL; /* forgot to run drm_fbconv_fill_fb_info()? */ + modeset->dev = dev; modeset->fb_info = fb_info;
@@ -1820,3 +1824,122 @@ drm_fbconv_modeset_setup_pipe(struct drm_fbconv_modeset *modeset, return 0; } EXPORT_SYMBOL(drm_fbconv_modeset_setup_pipe); + +/* + * Helpers for struct fb_info + * + * This is the setup and cleanup code for struct fb_info. It has been + * adapted from the fbdev core modules. + * + * The original implementation in fbdev also handles device files, console, + * and framebuffer events. As DRM drivers use DRM's framebuffer emulation, + * the respective code has been removed here. + * + * In contrast to the fbdev core, we don't need locking here. These don't + * interact with fbdev's internal state. + */ + +static bool is_matroxfb(const struct fb_info *fb_info) +{ + return !!(fb_info->fix.accel & (FB_ACCEL_MATROX_MGA2064W | + FB_ACCEL_MATROX_MGA1064SG | + FB_ACCEL_MATROX_MGA2164W | + FB_ACCEL_MATROX_MGA2164W_AGP | + FB_ACCEL_MATROX_MGAG100 | + FB_ACCEL_MATROX_MGAG200 | + FB_ACCEL_MATROX_MGAG400)); +} + +/** + * drm_fbconv_fill_fb_info - prepares an fbdev driver's fb_info structure + * for use + * @fb_info: the fb_info structure to set up + * Returns: + * 0 on success, or + * a negative error code otherwise. + * + * The fbdev driver provides fbconv helpers with an fb_info structure. Before + * use, the structure has to be set up correctly. In fbdev core, + * register_framebuffer() does this; here it's provided by + * drm_fbconv_fill_fb_info(). + * + * Call drm_fbconv_cleanup_fb_info() during shutdown to clean up the fb_info + * structure. + */ +int drm_fbconv_fill_fb_info(struct fb_info *fb_info) +{ + struct fb_videomode mode; + + /* Returing -ENOSYS on error is technically wrong, but it's + * what the fbdev core code would do. So we do the same. + */ + if (fb_check_foreignness(fb_info)) + return -ENOSYS; + + fb_info->node = FB_MAX; /* not registered; filled by fbconv helpers */ + atomic_set(&fb_info->count, 1); + mutex_init(&fb_info->lock); + mutex_init(&fb_info->mm_lock); + + fb_info->dev = NULL; /* device file not needed for fbconv. */ + + if (fb_info->pixmap.addr == NULL) { + fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); + if (fb_info->pixmap.addr) { + fb_info->pixmap.size = FBPIXMAPSIZE; + fb_info->pixmap.buf_align = 1; + fb_info->pixmap.scan_align = 1; + fb_info->pixmap.access_align = 32; + fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; + } + } + fb_info->pixmap.offset = 0; + + if (!fb_info->pixmap.blit_x) + fb_info->pixmap.blit_x = ~(u32)0; + + if (!fb_info->pixmap.blit_y) + fb_info->pixmap.blit_y = ~(u32)0; + + if (!fb_info->modelist.prev || !fb_info->modelist.next) + INIT_LIST_HEAD(&fb_info->modelist); + + fb_var_to_videomode(&mode, &fb_info->var); + fb_add_videomode(&mode, &fb_info->modelist); + + /* matroxfb: Requries a call to fb_set_par() to initialize + * fbinfo->fix.{smem_start,smem_len}. Otherwise, we won't be + * able to initialize framebuffer memory management. + */ + if (is_matroxfb(fb_info)) { + if (fb_info->fbops->fb_set_par) + fb_info->fbops->fb_set_par(fb_info); + } + + return 0; +} +EXPORT_SYMBOL(drm_fbconv_fill_fb_info); + +/** + * drm_fbconv_cleanup_fb_info - cleans up an fbdev driver's fb_info structure + * after use + * @fb_info: the fb_info structure to clean up + */ +void drm_fbconv_cleanup_fb_info(struct fb_info *fb_info) +{ + if (fb_info->node != FB_MAX) + return; + + if (fb_info->pixmap.addr && + (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) + kfree(fb_info->pixmap.addr); + + fb_destroy_modelist(&fb_info->modelist); + + if (!atomic_dec_and_test(&fb_info->count)) + return; + + if (fb_info->fbops->fb_destroy) + fb_info->fbops->fb_destroy(fb_info); +} +EXPORT_SYMBOL(drm_fbconv_cleanup_fb_info); diff --git a/include/drm/drm_fbconv_helper.h b/include/drm/drm_fbconv_helper.h index c7d211f40462..23d17ad14b81 100644 --- a/include/drm/drm_fbconv_helper.h +++ b/include/drm/drm_fbconv_helper.h @@ -140,4 +140,11 @@ int drm_fbconv_modeset_setup_pipe( const uint32_t *formats, unsigned int format_count, const uint64_t *format_modifiers, struct drm_connector *connector);
+/* + * Helpers for struct fb_info + */ + +int drm_fbconv_fill_fb_info(struct fb_info *fb_info); +void drm_fbconv_cleanup_fb_info(struct fb_info *fb_info); + #endif
There's now a tutorial on how to create a DRM driver on top of fbconv helpers. The DRM TODO list contains an entry for converting fbdev drivers over to DRM.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- Documentation/gpu/drm-kms-helpers.rst | 12 ++ Documentation/gpu/todo.rst | 15 +++ drivers/gpu/drm/drm_fbconv_helper.c | 181 ++++++++++++++++++++++++++ 3 files changed, 208 insertions(+)
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 9668a7fe2408..1232a3ef24ff 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -411,3 +411,15 @@ SHMEM GEM Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c :export: + +fbdev Conversion Helper Reference +================================= + +.. kernel-doc:: drivers/gpu/drm/drm_fbconv_helper.c + :doc: fbconv helpers + +.. kernel-doc:: include/drm/drm_fbconv_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_fbconv_helper.c + :export: diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 79785559d711..1be44a17f3e8 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -462,3 +462,18 @@ Contact: Sam Ravnborg
Outside DRM =========== + +Convert fbdev drivers to DRM +---------------------------- + +There are plenty of fbdev drivers for old hardware. With fbconv helpers, we +have a simple and clean way of transitioning fbdev drivers to DRM. Set up a +simple DRM driver that builds onto the fbconv helpers, copy over the fbdev +driver and connect both. This should result in a basic DRM driver that can +run X11 and Weston. There's a tutorial for this process with example source +code in the fbconv documentation. + +From there, refactor the driver source code into a clean DRM driver that +requires neither fbdev nor fbconv helpers. + +Contact: Thomas Zimmermann diff --git a/drivers/gpu/drm/drm_fbconv_helper.c b/drivers/gpu/drm/drm_fbconv_helper.c index 7d7e4da2a29e..1fa240a4789f 100644 --- a/drivers/gpu/drm/drm_fbconv_helper.c +++ b/drivers/gpu/drm/drm_fbconv_helper.c @@ -18,6 +18,187 @@ #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h>
+/** + * DOC: fbconv helpers + * + * The Linux kernel's fbdev subsystem provides a wide range of drivers for + * older graphics hardware. Except for these existng drivers, fbdev is + * deprecated and expected to be removed at some point in the future. All new + * development happens in DRM. Some of the fbdev drivers are worth carrying + * forward. The fbconv helper functions provide a framework for porting fbdev + * drivers to DRM. + * + * When porting over fbdev drivers to DRM, the most significant problem is the + * difference in how the internal driver interfaces work. Fbdev has a single + * function, struct fb_ops.fb_set_par(), to set video mode and framebuffer + * format. DRM use a much more fine-grained interface. In fbdev, framebuffer + * memory is managed by a single client, while in DRM multiple clients can + * hold buffers with framebuffer data. + * + * The fbconv helper library provides a set of data structures and functions + * that connect DRM and fbdev. The resulting DRM driver maps DRM operations + * to fbdev interfaces and uses an fbdev driver for its hardware operations. + * Such a driver is not intended to be merged into DRM as-is. It does, + * however, provide a starting point for refactoring the fbdev driver's + * implementation into first-class DRM code. + * + * As an example, we create a DRM driver from vesafb, fbdev's generic + * vesa driver. We begin by creating a DRM stub driver vesadrm. Please keep + * in mind that the provided code is for illustrative purposes and requires + * error handling. + * + * .. code-block:: c + * + * DEFINE_DRM_GEM_SHMEM_FOPS(vesadrm_file_operations); + * + * static struct drm_driver vesadrm_driver = { + * .major = 1, + * .minor = 0, + * .patchlevel = 0, + * .name = "vesadrm", + * .desc = "DRM VESA driver", + * .date = "01/01/1970", + * .driver_features = DRIVER_ATOMIC | + * DRIVER_GEM | + * DRIVER_MODESET, + * .fops = &vesadrm_file_operations, + * DRM_GEM_SHMEM_DRIVER_OPS, + * }; + * + * Fbconv uses SHMEM, so we set up the structures accordingly. + * + * The fbdev code usually calls register_framebuffer() and + * unregister_framebuffer() to connect and disconnect itself to the fbdev + * core code. In our case, we replace these calls with + * vesadrm_register_framebuffer() and vesadrm_unregister_framebuffer(), which + * serve as entry points for vesafb. + * + * .. code-block:: c + * + * #include <drm/drm/fbconv_helper.h> + * + * struct vesadrm_device { + * struct drm_device dev; + * struct fb_info *fb_info; + * + * struct drm_fbconv_modeset modeset; + * }; + * + * struct vesadrm_device* vesadrm_register_framebuffer(struct fb_info *fb_info) + * { + * struct vesadrm *vdev; + * + * drm_fbconv_fill_fb_info(fb_info); + * + * vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + * vesadrm_device_init(vdev, &vesadrm_driver, fb_info) + * + * drm_dev_register(&vdev->dev, 0); + * + * return vdev; + * } + * + * Here, we have the first references to fbconf helpers. The instance + * of struct drm_fbconv_modeset is the central data structure for fbconv. + * Built upon struct drm_simple_display_pipe, it stores most state for the + * DRM driver. + * + * The function vesadrm_register_framebuffer() will later be called by + * vesafb code with the fbdev driver's fb_info structure. In core fbdev, + * register_framebuffer() would fill fb_info with general state and complete + * registration. With fbconv helpers, drm_fbconv_fill_fb_info() does this. + * It's a simplified version of the fbdev setup process, without device file + * creation, registration, or events. No console is created either. + * Finally vesadrm_register_framebuffer() initializes the vesadrm device and + * registers the DRM device. At this point, vesadrm is completely initialized. + * + * For completeness, here's the implementation of + * vesadrm_unregister_framebuffer(), which shuts the device down. + * + * .. code-block:: c + * + * void vesadrm_unregister_framebuffer(struct vesadrm_device *vdev) + * { + * struct fb_info *fb_info = vdev->fb_info; + * + * vesadrm_device_cleanup(vdev); + * kfree(vdev); + * drm_fbconv_cleanup_fb_info(fb_info); + * } + * + * Next we need an implementation of vesadrm_device_init() and + * vesadrm_device_cleanup(). These functions handle the details of + * device configuration and console setup. As all this functionality + * is provided by helpers, the actual implementation is fairly small. + * + * .. code-block:: c + * + * static int vesadrm_device_init(struct vesadrm_device *vdev, + * struct drm_driver *drv, + * struct fb_info *fb_info) + * { + * static const uint32_t formats[] = { + * DRM_FORMAT_XRGB8888, + * DRM_FORMAT_RGB565 + * }; + * static unsigned int max_width = 1600; + * static unsigned int max_height = 1200; + * static unsigned int preferred_depth = 32; + * + * drm_dev_init(&vdev->dev, drv, fb_info->device); + * + * vdev->dev.dev_private = vdev; + * vdev->dev.pdev = container_of(fb_info->device, struct pci_dev, dev); + * vdev->fb_info = fb_info; + * + * drm_fbconv_modeset_init(&vdev->modeset, &vdev->dev, fb_info, + * max_width, max_height, preferred_depth); + * + * drm_fbconv_modeset_setup_pipe(&vdev->modeset, NULL, formats, + * ARRAY_SIZE(formats), NULL, NULL); + * + * drm_fbdev_generic_setup(&vdev->dev, 0); + * + * return 0; + * } + * + * static void vesadrm_device_cleanup(struct vesadrm_device *vdev) + * { + * struct drm_device *dev = &vdev->dev; + * + * drm_fbconv_modeset_cleanup(&vdev->modeset); + * + * drm_dev_fini(dev); + * dev->dev_private = NULL; + * } + * + * In vesadrm_device_init(), several device-specific constants are declared. + * Depending on the hardware, drivers should set them accordingly. + * The call to drm_fbconv_modeset_init() initializes fbconv modesetting + * helpers with these device constants. + * + * The drm_fbconv_modeset_setup_pipe() creates the simple display pipe with + * the specified color formats. By default, everything is set up + * automatically. But the function also accepts format modifiers, a DRM + * connector, and call-back functions for struct drm_simple_display_pipe. + * So each of these can be refactored individually later on. + * + * After setting up the fbconv helpers, there's is a call to + * drm_fbdev_generic_setup(), which set an initial mode and creates a + * framebuffer console. + * + * The implementation of vesadrm_device_cleanup() is the inverse of the + * init function. It cleans up the fbconv modesetting helper and releases + * the DRM device. + * + * What is left is connecting vesafb to vesadrm. As a first step, we need a + * copy the vesafb source files into the vesadrm driver and make them compile. + * Once this is done, we have to replace the call to register_framebuffer() + * with a call to vesadrm_register_framebuffer(), and unregister_framebuffer() + * with vesadrm_unregister_framebuffer(). We have now disconnected vesafb from + * the fbdev core and run it as part of DRM. + */ + /* * Format conversion helpers */
Hi Thomas,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Thomas-Zimmermann/DRM-fbconv-helper... reproduce: make htmldocs
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All warnings (new ones prefixed by >>):
include/linux/skbuff.h:888: warning: Function parameter or member 'vlan_present' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'csum_complete_sw' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'csum_level' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'inner_protocol_type' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'remcsum_offload' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'sender_cpu' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'reserved_tailroom' not described in 'sk_buff' include/linux/skbuff.h:888: warning: Function parameter or member 'inner_ipproto' not described in 'sk_buff' include/net/sock.h:233: warning: Function parameter or member 'skc_addrpair' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_portpair' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_ipv6only' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_net_refcnt' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_v6_daddr' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_v6_rcv_saddr' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_cookie' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_listener' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_tw_dr' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_rcv_wnd' not described in 'sock_common' include/net/sock.h:233: warning: Function parameter or member 'skc_tw_rcv_nxt' not described in 'sock_common' include/net/sock.h:515: warning: Function parameter or member 'sk_rx_skb_cache' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_wq_raw' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'tcp_rtx_queue' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_tx_skb_cache' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_route_forced_caps' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_txtime_report_errors' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_validate_xmit_skb' not described in 'sock' include/net/sock.h:515: warning: Function parameter or member 'sk_bpf_storage' not described in 'sock' include/net/sock.h:2439: warning: Function parameter or member 'tcp_rx_skb_cache_key' not described in 'DECLARE_STATIC_KEY_FALSE' include/net/sock.h:2439: warning: Excess function parameter 'sk' description in 'DECLARE_STATIC_KEY_FALSE' include/net/sock.h:2439: warning: Excess function parameter 'skb' description in 'DECLARE_STATIC_KEY_FALSE' include/linux/netdevice.h:2053: warning: Function parameter or member 'gso_partial_features' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'l3mdev_ops' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'xfrmdev_ops' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'tlsdev_ops' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'name_assign_type' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'ieee802154_ptr' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'mpls_ptr' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'xdp_prog' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'gro_flush_timeout' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'nf_hooks_ingress' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member '____cacheline_aligned_in_smp' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'qdisc_hash' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'xps_cpus_map' not described in 'net_device' include/linux/netdevice.h:2053: warning: Function parameter or member 'xps_rxqs_map' not described in 'net_device' include/linux/phylink.h:56: warning: Function parameter or member '__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising' not described in 'phylink_link_state' include/linux/phylink.h:56: warning: Function parameter or member '__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising' not described in 'phylink_link_state' drivers/net/phy/phylink.c:595: warning: Function parameter or member 'config' not described in 'phylink_create' drivers/net/phy/phylink.c:595: warning: Excess function parameter 'ndev' description in 'phylink_create' lib/genalloc.c:1: warning: 'gen_pool_add_virt' not found lib/genalloc.c:1: warning: 'gen_pool_alloc' not found lib/genalloc.c:1: warning: 'gen_pool_free' not found lib/genalloc.c:1: warning: 'gen_pool_alloc_algo' not found include/linux/rculist.h:374: warning: Excess function parameter 'cond' description in 'list_for_each_entry_rcu' include/linux/rculist.h:651: warning: Excess function parameter 'cond' description in 'hlist_for_each_entry_rcu' mm/util.c:1: warning: 'get_user_pages_fast' not found drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c:335: warning: Excess function parameter 'dev' description in 'amdgpu_gem_prime_export' drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c:336: warning: Excess function parameter 'dev' description in 'amdgpu_gem_prime_export' drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:142: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_read_lock' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:347: warning: cannot understand function prototype: 'struct amdgpu_vm_pt_cursor ' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:348: warning: cannot understand function prototype: 'struct amdgpu_vm_pt_cursor ' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:494: warning: Function parameter or member 'start' not described in 'amdgpu_vm_pt_first_dfs' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:546: warning: Function parameter or member 'adev' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:546: warning: Function parameter or member 'vm' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:546: warning: Function parameter or member 'start' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:546: warning: Function parameter or member 'cursor' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:546: warning: Function parameter or member 'entry' not described in 'for_each_amdgpu_vm_pt_dfs_safe' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:821: warning: Function parameter or member 'level' not described in 'amdgpu_vm_bo_param' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'params' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'bo' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'level' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'pe' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'addr' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'count' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'incr' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:1283: warning: Function parameter or member 'flags' not described in 'amdgpu_vm_update_flags' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2821: warning: Function parameter or member 'pasid' not described in 'amdgpu_vm_make_compute' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:378: warning: Excess function parameter 'entry' description in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:379: warning: Function parameter or member 'ih' not described in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c:379: warning: Excess function parameter 'entry' description in 'amdgpu_irq_dispatch' drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c:1: warning: no structured comments found drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c:1: warning: no structured comments found drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c:1: warning: 'pp_dpm_sclk pp_dpm_mclk pp_dpm_pcie' not found drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:132: warning: Incorrect use of kernel-doc format: * @atomic_obj drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:238: warning: Incorrect use of kernel-doc format: * gpu_info FW provided soc bounding box struct or 0 if not drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'atomic_obj' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'backlight_link' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'backlight_caps' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'freesync_module' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'fw_dmcu' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'dmcu_fw_version' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:243: warning: Function parameter or member 'soc_bounding_box' not described in 'amdgpu_display_manager' drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1: warning: 'dm_crtc_high_irq' not found drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1: warning: 'dm_pflip_high_irq' not found drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c:1: warning: 'register_hpd_handlers' not found include/drm/drm_modeset_helper_vtables.h:1053: warning: Function parameter or member 'prepare_writeback_job' not described in 'drm_connector_helper_funcs' include/drm/drm_modeset_helper_vtables.h:1053: warning: Function parameter or member 'cleanup_writeback_job' not described in 'drm_connector_helper_funcs' include/drm/drm_atomic_state_helper.h:1: warning: no structured comments found include/drm/drm_gem_shmem_helper.h:87: warning: Function parameter or member 'madv' not described in 'drm_gem_shmem_object' include/drm/drm_gem_shmem_helper.h:87: warning: Function parameter or member 'madv_list' not described in 'drm_gem_shmem_object' drivers/gpu/drm/drm_fbconv_helper.c:370: warning: Excess function parameter 'fb_info' description in 'drm_fbconv_format_of_fb_var_screeninfo'
include/drm/drm_fbconv_helper.h:124: warning: Function parameter or member 'blit' not described in 'drm_fbconv_modeset' drivers/gpu/drm/drm_fbconv_helper.c:371: warning: Function parameter or member 'fb_var' not described in 'drm_fbconv_format_of_fb_var_screeninfo'
drivers/gpu/drm/drm_fbconv_helper.c:371: warning: Excess function parameter 'fb_info' description in 'drm_fbconv_format_of_fb_var_screeninfo' drivers/gpu/drm/i915/display/intel_dpll_mgr.h:158: warning: Enum value 'DPLL_ID_TGL_MGPLL5' not described in enum 'intel_dpll_id' drivers/gpu/drm/i915/display/intel_dpll_mgr.h:158: warning: Enum value 'DPLL_ID_TGL_MGPLL6' not described in enum 'intel_dpll_id' drivers/gpu/drm/i915/display/intel_dpll_mgr.h:158: warning: Excess enum value 'DPLL_ID_TGL_TCPLL5' description in 'intel_dpll_id' drivers/gpu/drm/i915/display/intel_dpll_mgr.h:158: warning: Excess enum value 'DPLL_ID_TGL_TCPLL6' description in 'intel_dpll_id' drivers/gpu/drm/i915/display/intel_dpll_mgr.h:342: warning: Function parameter or member 'wakeref' not described in 'intel_shared_dpll' Error: Cannot open file drivers/gpu/drm/i915/i915_gem_batch_pool.c Error: Cannot open file drivers/gpu/drm/i915/i915_gem_batch_pool.c Error: Cannot open file drivers/gpu/drm/i915/i915_gem_batch_pool.c drivers/gpu/drm/i915/i915_drv.h:1129: warning: Incorrect use of kernel-doc format: * The OA context specific information. drivers/gpu/drm/i915/i915_drv.h:1143: warning: Incorrect use of kernel-doc format: * State of the OA buffer. drivers/gpu/drm/i915/i915_drv.h:1154: warning: Incorrect use of kernel-doc format: * Locks reads and writes to all head/tail state drivers/gpu/drm/i915/i915_drv.h:1176: warning: Incorrect use of kernel-doc format: * One 'aging' tail pointer and one 'aged' tail pointer ready to drivers/gpu/drm/i915/i915_drv.h:1188: warning: Incorrect use of kernel-doc format: * Index for the aged tail ready to read() data up to. drivers/gpu/drm/i915/i915_drv.h:1193: warning: Incorrect use of kernel-doc format: * A monotonic timestamp for when the current aging tail pointer drivers/gpu/drm/i915/i915_drv.h:1199: warning: Incorrect use of kernel-doc format: * Although we can always read back the head pointer register, drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'pinned_ctx' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'specific_ctx_id' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'specific_ctx_id_mask' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'poll_check_timer' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'poll_wq' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'pollin' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'periodic' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'period_exponent' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1207: warning: Function parameter or member 'oa_buffer' not described in 'i915_perf_stream' drivers/gpu/drm/i915/i915_drv.h:1129: warning: Incorrect use of kernel-doc format: * The OA context specific information. drivers/gpu/drm/i915/i915_drv.h:1143: warning: Incorrect use of kernel-doc format: * State of the OA buffer. drivers/gpu/drm/i915/i915_drv.h:1154: warning: Incorrect use of kernel-doc format: * Locks reads and writes to all head/tail state drivers/gpu/drm/i915/i915_drv.h:1176: warning: Incorrect use of kernel-doc format: * One 'aging' tail pointer and one 'aged' tail pointer ready to drivers/gpu/drm/i915/i915_drv.h:1188: warning: Incorrect use of kernel-doc format: * Index for the aged tail ready to read() data up to. drivers/gpu/drm/i915/i915_drv.h:1193: warning: Incorrect use of kernel-doc format: * A monotonic timestamp for when the current aging tail pointer drivers/gpu/drm/i915/i915_drv.h:1199: warning: Incorrect use of kernel-doc format: * Although we can always read back the head pointer register, drivers/gpu/drm/i915/i915_drv.h:1129: warning: Incorrect use of kernel-doc format: * The OA context specific information. drivers/gpu/drm/i915/i915_drv.h:1143: warning: Incorrect use of kernel-doc format: * State of the OA buffer. drivers/gpu/drm/i915/i915_drv.h:1154: warning: Incorrect use of kernel-doc format: * Locks reads and writes to all head/tail state drivers/gpu/drm/i915/i915_drv.h:1176: warning: Incorrect use of kernel-doc format: * One 'aging' tail pointer and one 'aged' tail pointer ready to drivers/gpu/drm/i915/i915_drv.h:1188: warning: Incorrect use of kernel-doc format: * Index for the aged tail ready to read() data up to. drivers/gpu/drm/i915/i915_drv.h:1193: warning: Incorrect use of kernel-doc format: * A monotonic timestamp for when the current aging tail pointer drivers/gpu/drm/i915/i915_drv.h:1199: warning: Incorrect use of kernel-doc format: * Although we can always read back the head pointer register, drivers/gpu/drm/mcde/mcde_drv.c:1: warning: 'ST-Ericsson MCDE DRM Driver' not found include/net/cfg80211.h:1185: warning: Function parameter or member 'txpwr' not described in 'station_parameters' include/net/mac80211.h:4056: warning: Function parameter or member 'sta_set_txpwr' not described in 'ieee80211_ops' include/net/mac80211.h:2018: warning: Function parameter or member 'txpwr' not described in 'ieee80211_sta' Documentation/admin-guide/perf/imx-ddr.rst:21: WARNING: Unexpected indentation. Documentation/admin-guide/perf/imx-ddr.rst:34: WARNING: Unexpected indentation. Documentation/admin-guide/perf/imx-ddr.rst:40: WARNING: Unexpected indentation. Documentation/admin-guide/perf/imx-ddr.rst:45: WARNING: Unexpected indentation. Documentation/admin-guide/perf/imx-ddr.rst:52: WARNING: Unexpected indentation. Documentation/admin-guide/xfs.rst:257: WARNING: Block quote ends without a blank line; unexpected unindent. Documentation/filesystems/ubifs-authentication.rst:94: WARNING: Inline interpreted text or phrase reference start-string without end-string. Documentation/usb/index.rst:5: WARNING: toctree contains reference to nonexisting document 'usb/rio' Documentation/usb/index.rst:5: WARNING: toctree contains reference to nonexisting document 'usb/wusb-design-overview' Documentation/usb/text_files.rst:22: WARNING: Include file 'Documentation/usb/wusb-cbaf' not found or reading it failed Documentation/trace/kprobetrace.rst:100: WARNING: Explicit markup ends without a blank line; unexpected unindent. WARNING: kernel-doc 'scripts/kernel-doc -rst -enable-lineno -function Reservation Object Overview drivers/dma-buf/reservation.c' failed with return code 1 WARNING: kernel-doc 'scripts/kernel-doc -rst -enable-lineno -export drivers/dma-buf/reservation.c' failed with return code 2 WARNING: kernel-doc 'scripts/kernel-doc -rst -enable-lineno -internal include/linux/reservation.h' failed with return code 2 Documentation/translations/it_IT/process/maintainer-pgp-guide.rst:458: WARNING: Unknown target name: "nitrokey pro". Documentation/security/keys/core.rst:1110: WARNING: Inline emphasis start-string without end-string. Documentation/security/keys/core.rst:1110: WARNING: Inline emphasis start-string without end-string. Documentation/security/keys/core.rst:1108: WARNING: Inline emphasis start-string without end-string. Documentation/security/keys/core.rst:1108: WARNING: Inline emphasis start-string without end-string. Documentation/security/keys/core.rst:1108: WARNING: Inline emphasis start-string without end-string. include/uapi/linux/firewire-cdev.h:312: WARNING: Inline literal start-string without end-string. drivers/firewire/core-transaction.c:606: WARNING: Inline strong start-string without end-string. drivers/ata/libata-core.c:5945: WARNING: Unknown target name: "hw". drivers/message/fusion/mptbase.c:5057: WARNING: Definition list ends without a blank line; unexpected unindent. include/linux/regulator/driver.h:284: WARNING: Unknown target name: "regulator_regmap_x_voltage". include/linux/spi/spi.h:382: WARNING: Unexpected indentation. fs/seq_file.c:40: WARNING: Inline strong start-string without end-string. fs/seq_file.c:40: WARNING: Inline strong start-string without end-string. fs/seq_file.c:40: WARNING: Inline strong start-string without end-string. fs/seq_file.c:40: WARNING: Inline strong start-string without end-string. fs/posix_acl.c:636: WARNING: Inline emphasis start-string without end-string. fs/debugfs/inode.c:427: WARNING: Inline literal start-string without end-string. fs/debugfs/inode.c:506: WARNING: Inline literal start-string without end-string. fs/debugfs/inode.c:538: WARNING: Inline literal start-string without end-string. fs/debugfs/inode.c:631: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:424: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:430: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:469: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:475: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:514: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:520: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:560: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:566: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:608: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:614: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:875: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:881: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:928: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:934: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:1120: WARNING: Inline literal start-string without end-string. fs/debugfs/file.c:1126: WARNING: Inline literal start-string without end-string. Documentation/networking/device_drivers/pensando/ionic.rst:39: WARNING: Unexpected indentation. Documentation/networking/device_drivers/pensando/ionic.rst:43: WARNING: Unexpected indentation. Documentation/misc-devices/index.rst:14: WARNING: toctree contains reference to nonexisting document 'misc-devices/xilinx_sdfec' Documentation/driver-api/gpio/driver.rst:420: WARNING: Unexpected indentation. Documentation/driver-api/gpio/driver.rst:418: WARNING: Inline emphasis start-string without end-string. Documentation/driver-api/gpio/driver.rst:422: WARNING: Block quote ends without a blank line; unexpected unindent.
vim +124 include/drm/drm_fbconv_helper.h
87f9f686c7da49 Thomas Zimmermann 2019-10-14 @124
:::::: The code at line 124 was first introduced by commit :::::: 87f9f686c7da49f2b145d7c1073bb38319b31f99 drm/fbconv: Add modesetting infrastructure
:::::: TO: Thomas Zimmermann tzimmermann@suse.de :::::: CC: 0day robot lkp@intel.com
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
The mgakms driver uses DRM's fbconv helpers to provide a DRM driver for Matrox chipsets. This will allow matroxfb to be refactored into a first-class DRM driver.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/mgakms/Kconfig | 13 +++ drivers/staging/mgakms/Makefile | 6 ++ drivers/staging/mgakms/mga_device.c | 68 +++++++++++++++ drivers/staging/mgakms/mga_device.h | 30 +++++++ drivers/staging/mgakms/mga_drv.c | 129 ++++++++++++++++++++++++++++ drivers/staging/mgakms/mga_drv.h | 14 +++ 8 files changed, 263 insertions(+) create mode 100644 drivers/staging/mgakms/Kconfig create mode 100644 drivers/staging/mgakms/Makefile create mode 100644 drivers/staging/mgakms/mga_device.c create mode 100644 drivers/staging/mgakms/mga_device.h create mode 100644 drivers/staging/mgakms/mga_drv.c create mode 100644 drivers/staging/mgakms/mga_drv.h
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6f1fa4c849a1..fd25596813c5 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -125,4 +125,6 @@ source "drivers/staging/exfat/Kconfig"
source "drivers/staging/qlge/Kconfig"
+source "drivers/staging/mgakms/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a90f9b308c8d..4c98b028ee99 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_UWB) += uwb/ obj-$(CONFIG_USB_WUSB) += wusbcore/ obj-$(CONFIG_EXFAT_FS) += exfat/ obj-$(CONFIG_QLGE) += qlge/ +obj-$(CONFIG_DRM_MGAKMS) += mgakms/ diff --git a/drivers/staging/mgakms/Kconfig b/drivers/staging/mgakms/Kconfig new file mode 100644 index 000000000000..de23e76317bd --- /dev/null +++ b/drivers/staging/mgakms/Kconfig @@ -0,0 +1,13 @@ +config DRM_MGAKMS + tristate "Matrox g200/g400" + depends on DRM && PCI + select DRM_FBCONV_HELPER + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + Choose this option if you have a Matrox Millennium, + Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, + Matrox Productiva G100, Matrox Mystique G200, + Matrox Millennium G200, Matrox Marvel G200 video, Matrox G400, + G450 or G550 card. If M is selected, the module will be called mga. + AGP support is required for this driver to work. diff --git a/drivers/staging/mgakms/Makefile b/drivers/staging/mgakms/Makefile new file mode 100644 index 000000000000..65695f04eb7f --- /dev/null +++ b/drivers/staging/mgakms/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 + +mgakms-y := mga_device.o \ + mga_drv.o + +obj-$(CONFIG_DRM_MGAKMS) += mgakms.o diff --git a/drivers/staging/mgakms/mga_device.c b/drivers/staging/mgakms/mga_device.c new file mode 100644 index 000000000000..34b3bb1ed8a5 --- /dev/null +++ b/drivers/staging/mgakms/mga_device.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/fb.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_modeset_helper.h> + +#include "mga_device.h" + +/* + * struct mga_device + */ + +int mga_device_init(struct mga_device *mdev, struct drm_driver *drv, + struct fb_info *fb_info) +{ + static const uint32_t formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565 + }; + static unsigned int max_width = 2048; + static unsigned int max_height = 2048; + static unsigned int preferred_depth = 32; + + int ret; + + ret = drm_dev_init(&mdev->dev, drv, fb_info->device); + if (ret) + return ret; + mdev->dev.dev_private = mdev; + mdev->dev.pdev = container_of(fb_info->device, struct pci_dev, dev); + mdev->fb_info = fb_info; + + ret = drm_fbconv_modeset_init(&mdev->modeset, &mdev->dev, fb_info, + max_width, max_height, preferred_depth); + if (ret) + goto err_drm_dev_fini; + + ret = drm_fbconv_modeset_setup_pipe(&mdev->modeset, NULL, formats, + ARRAY_SIZE(formats), NULL, NULL); + if (ret) + goto err_drm_fbconv_modeset_cleanup; + + ret = drm_fbdev_generic_setup(&mdev->dev, 0); + if (ret) + goto err_drm_fbconv_modeset_cleanup; + + return 0; + +err_drm_fbconv_modeset_cleanup: + /* cleans up all mode-setting structures */ + drm_fbconv_modeset_cleanup(&mdev->modeset); +err_drm_dev_fini: + drm_dev_fini(&mdev->dev); + return ret; +} + +void mga_device_cleanup(struct mga_device *mdev) +{ + struct drm_device *dev = &mdev->dev; + + drm_fbconv_modeset_cleanup(&mdev->modeset); + + drm_dev_fini(dev); + dev->dev_private = NULL; +} diff --git a/drivers/staging/mgakms/mga_device.h b/drivers/staging/mgakms/mga_device.h new file mode 100644 index 000000000000..442effbf37bc --- /dev/null +++ b/drivers/staging/mgakms/mga_device.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef MGA_DEVICE_H +#define MGA_DEVICE_H + +#include <linux/kernel.h> + +#include <drm/drm_device.h> +#include <drm/drm_fbconv_helper.h> + +struct drm_driver; +struct fb_info; + +struct mga_device { + struct drm_device dev; + struct fb_info *fb_info; + + struct drm_fbconv_modeset modeset; +}; + +static inline struct mga_device *mga_device_of_dev(struct drm_device *dev) +{ + return container_of(dev, struct mga_device, dev); +} + +int mga_device_init(struct mga_device *mdev, struct drm_driver *drv, + struct fb_info *fb_info); +void mga_device_cleanup(struct mga_device *mdev); + +#endif diff --git a/drivers/staging/mgakms/mga_drv.c b/drivers/staging/mgakms/mga_drv.c new file mode 100644 index 000000000000..75e26d3046f3 --- /dev/null +++ b/drivers/staging/mgakms/mga_drv.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/fb.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_fbconv_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_file.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_ioctl.h> + +#include "mga_device.h" +#include "mga_drv.h" + +#define DRIVER_AUTHOR "Thomas Zimmermann tzimmermann@suse.de" +#define DRIVER_NAME "mgakms" +#define DRIVER_DESCRIPTION "DRM driver for Matrox graphics chipsets" +#define DRIVER_LICENSE "GPL" +#define DRIVER_DATE "20190301" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 1 + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_SHMEM_FOPS(mga_file_operations); + +static struct drm_driver mga_driver = { + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .name = DRIVER_NAME, + .desc = DRIVER_DESCRIPTION, + .date = DRIVER_DATE, + .driver_features = DRIVER_ATOMIC | + DRIVER_GEM | + DRIVER_MODESET, + .fops = &mga_file_operations, + DRM_GEM_SHMEM_DRIVER_OPS, +}; + +static void mga_remove_conflicting_framebuffers(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, 1); + ap->ranges[0].size = pci_resource_len(pdev, 1); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & + IORESOURCE_ROM_SHADOW; +#endif + drm_fb_helper_remove_conflicting_framebuffers(ap, DRIVER_NAME, + primary); + kfree(ap); +} + +static struct mga_device *mga_create_device(struct fb_info *fb_info) +{ + struct mga_device *mdev; + int ret; + + mdev = devm_kzalloc(fb_info->device, sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return ERR_PTR(-ENOMEM); + ret = mga_device_init(mdev, &mga_driver, fb_info); + if (ret) + goto err_devm_kfree; + return mdev; + +err_devm_kfree: + devm_kfree(fb_info->device, mdev); + return ERR_PTR(ret); +} + +static void mga_destroy_device(struct mga_device *mdev) +{ + struct device *dev = mdev->fb_info->device; + + mga_device_cleanup(mdev); + devm_kfree(dev, mdev); +} + +struct mga_device *mga_register_framebuffer(struct fb_info *fb_info, + struct pci_dev *pdev) +{ + int ret; + struct mga_device *mdev; + + mga_remove_conflicting_framebuffers(pdev); + + ret = drm_fbconv_fill_fb_info(fb_info); + if (ret) + return ERR_PTR(ret); + + mdev = mga_create_device(fb_info); + if (IS_ERR(mdev)) { + ret = PTR_ERR(mdev); + goto err_drm_fbconv_cleanup_fb_info; + } + + ret = drm_dev_register(&mdev->dev, 0); + if (ret) + goto err_mga_destroy_device; + + return mdev; + +err_mga_destroy_device: + mga_destroy_device(mdev); +err_drm_fbconv_cleanup_fb_info: + drm_fbconv_cleanup_fb_info(fb_info); + return ERR_PTR(ret); +} + +void mga_unregister_framebuffer(struct mga_device *mdev) +{ + struct fb_info *fb_info = mdev->fb_info; + + mga_destroy_device(mdev); + drm_fbconv_cleanup_fb_info(fb_info); +} diff --git a/drivers/staging/mgakms/mga_drv.h b/drivers/staging/mgakms/mga_drv.h new file mode 100644 index 000000000000..d214719516c0 --- /dev/null +++ b/drivers/staging/mgakms/mga_drv.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef MGA_DRV_H +#define MGA_DRV_H + +struct fb_info; +struct mga_device; +struct pci_dev; + +struct mga_device *mga_register_framebuffer(struct fb_info *fb_info, + struct pci_dev *pdev); +void mga_unregister_framebuffer(struct mga_device *mdev); + +#endif
Only code is being copied, no functional changes are made.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/staging/mgakms/g450_pll.c | 539 +++++ drivers/staging/mgakms/g450_pll.h | 13 + drivers/staging/mgakms/i2c-matroxfb.c | 238 ++ drivers/staging/mgakms/matroxfb_DAC1064.c | 1113 +++++++++ drivers/staging/mgakms/matroxfb_DAC1064.h | 180 ++ drivers/staging/mgakms/matroxfb_Ti3026.c | 748 ++++++ drivers/staging/mgakms/matroxfb_Ti3026.h | 12 + drivers/staging/mgakms/matroxfb_accel.c | 519 ++++ drivers/staging/mgakms/matroxfb_accel.h | 9 + drivers/staging/mgakms/matroxfb_base.c | 2607 +++++++++++++++++++++ drivers/staging/mgakms/matroxfb_base.h | 710 ++++++ drivers/staging/mgakms/matroxfb_crtc2.h | 35 + drivers/staging/mgakms/matroxfb_g450.c | 640 +++++ drivers/staging/mgakms/matroxfb_g450.h | 15 + drivers/staging/mgakms/matroxfb_maven.h | 21 + drivers/staging/mgakms/matroxfb_misc.c | 815 +++++++ drivers/staging/mgakms/matroxfb_misc.h | 22 + 17 files changed, 8236 insertions(+) create mode 100644 drivers/staging/mgakms/g450_pll.c create mode 100644 drivers/staging/mgakms/g450_pll.h create mode 100644 drivers/staging/mgakms/i2c-matroxfb.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.h create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.c create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.h create mode 100644 drivers/staging/mgakms/matroxfb_accel.c create mode 100644 drivers/staging/mgakms/matroxfb_accel.h create mode 100644 drivers/staging/mgakms/matroxfb_base.c create mode 100644 drivers/staging/mgakms/matroxfb_base.h create mode 100644 drivers/staging/mgakms/matroxfb_crtc2.h create mode 100644 drivers/staging/mgakms/matroxfb_g450.c create mode 100644 drivers/staging/mgakms/matroxfb_g450.h create mode 100644 drivers/staging/mgakms/matroxfb_maven.h create mode 100644 drivers/staging/mgakms/matroxfb_misc.c create mode 100644 drivers/staging/mgakms/matroxfb_misc.h
diff --git a/drivers/staging/mgakms/g450_pll.c b/drivers/staging/mgakms/g450_pll.c new file mode 100644 index 000000000000..c15f8a57498e --- /dev/null +++ b/drivers/staging/mgakms/g450_pll.c @@ -0,0 +1,539 @@ +/* + * + * Hardware accelerated Matrox PCI cards - G450/G550 PLL control. + * + * (c) 2001-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.64 2002/06/10 + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + */ + +#include "g450_pll.h" +#include "matroxfb_DAC1064.h" + +static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) { + return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1); +} + +static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) { + return (p & 0x40) ? fin : fin << ((p & 3) + 1); +} + +static unsigned int g450_mnp2vco(const struct matrox_fb_info *minfo, + unsigned int mnp) +{ + unsigned int m, n; + + m = ((mnp >> 16) & 0x0FF) + 1; + n = ((mnp >> 7) & 0x1FE) + 4; + return (minfo->features.pll.ref_freq * n + (m >> 1)) / m; +} + +unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp) +{ + return g450_vco2f(mnp, g450_mnp2vco(minfo, mnp)); +} + +static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) { + if (f2 < f1) { + f2 = f1 - f2; + } else { + f2 = f2 - f1; + } + return f2; +} + +#define NO_MORE_MNP 0x01FFFFFF +#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */ + +static unsigned int g450_nextpll(const struct matrox_fb_info *minfo, + const struct matrox_pll_limits *pi, + unsigned int *fvco, unsigned int mnp) +{ + unsigned int m, n, p; + unsigned int tvco = *fvco; + + m = (mnp >> 16) & 0xFF; + p = mnp & 0xFF; + + do { + if (m == 0 || m == 0xFF) { + if (m == 0) { + if (p & 0x40) { + return NO_MORE_MNP; + } + if (p & 3) { + p--; + } else { + p = 0x40; + } + tvco >>= 1; + if (tvco < pi->vcomin) { + return NO_MORE_MNP; + } + *fvco = tvco; + } + + p &= 0x43; + if (tvco < 550000) { +/* p |= 0x00; */ + } else if (tvco < 700000) { + p |= 0x08; + } else if (tvco < 1000000) { + p |= 0x10; + } else if (tvco < 1150000) { + p |= 0x18; + } else { + p |= 0x20; + } + m = 9; + } else { + m--; + } + n = ((tvco * (m+1) + minfo->features.pll.ref_freq) / (minfo->features.pll.ref_freq * 2)) - 2; + } while (n < 0x03 || n > 0x7A); + return (m << 16) | (n << 8) | p; +} + +static unsigned int g450_firstpll(const struct matrox_fb_info *minfo, + const struct matrox_pll_limits *pi, + unsigned int *vco, unsigned int fout) +{ + unsigned int p; + unsigned int vcomax; + + vcomax = pi->vcomax; + if (fout > (vcomax / 2)) { + if (fout > vcomax) { + *vco = vcomax; + } else { + *vco = fout; + } + p = 0x40; + } else { + unsigned int tvco; + + p = 3; + tvco = g450_f2vco(p, fout); + while (p && (tvco > vcomax)) { + p--; + tvco >>= 1; + } + if (tvco < pi->vcomin) { + tvco = pi->vcomin; + } + *vco = tvco; + } + return g450_nextpll(minfo, pi, vco, 0xFF0000 | p); +} + +static inline unsigned int g450_setpll(const struct matrox_fb_info *minfo, + unsigned int mnp, unsigned int pll) +{ + switch (pll) { + case M_PIXEL_PLL_A: + matroxfb_DAC_out(minfo, M1064_XPIXPLLAM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLAN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLAP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_B: + matroxfb_DAC_out(minfo, M1064_XPIXPLLBM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLBN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLBP, mnp); + return M1064_XPIXPLLSTAT; + + case M_PIXEL_PLL_C: + matroxfb_DAC_out(minfo, M1064_XPIXPLLCM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XPIXPLLCN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XPIXPLLCP, mnp); + return M1064_XPIXPLLSTAT; + + case M_SYSTEM_PLL: + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLM, mnp >> 16); + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLN, mnp >> 8); + matroxfb_DAC_out(minfo, DAC1064_XSYSPLLP, mnp); + return DAC1064_XSYSPLLSTAT; + + case M_VIDEO_PLL: + matroxfb_DAC_out(minfo, M1064_XVIDPLLM, mnp >> 16); + matroxfb_DAC_out(minfo, M1064_XVIDPLLN, mnp >> 8); + matroxfb_DAC_out(minfo, M1064_XVIDPLLP, mnp); + return M1064_XVIDPLLSTAT; + } + return 0; +} + +static inline unsigned int g450_cmppll(const struct matrox_fb_info *minfo, + unsigned int mnp, unsigned int pll) +{ + unsigned char m = mnp >> 16; + unsigned char n = mnp >> 8; + unsigned char p = mnp; + + switch (pll) { + case M_PIXEL_PLL_A: + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLAM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLAN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLAP) != p); + + case M_PIXEL_PLL_B: + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLBM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLBN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLBP) != p); + + case M_PIXEL_PLL_C: + return (matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) != m || + matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) != n || + matroxfb_DAC_in(minfo, M1064_XPIXPLLCP) != p); + + case M_SYSTEM_PLL: + return (matroxfb_DAC_in(minfo, DAC1064_XSYSPLLM) != m || + matroxfb_DAC_in(minfo, DAC1064_XSYSPLLN) != n || + matroxfb_DAC_in(minfo, DAC1064_XSYSPLLP) != p); + + case M_VIDEO_PLL: + return (matroxfb_DAC_in(minfo, M1064_XVIDPLLM) != m || + matroxfb_DAC_in(minfo, M1064_XVIDPLLN) != n || + matroxfb_DAC_in(minfo, M1064_XVIDPLLP) != p); + } + return 1; +} + +static inline int g450_isplllocked(const struct matrox_fb_info *minfo, + unsigned int regidx) +{ + unsigned int j; + + for (j = 0; j < 1000; j++) { + if (matroxfb_DAC_in(minfo, regidx) & 0x40) { + unsigned int r = 0; + int i; + + for (i = 0; i < 100; i++) { + r += matroxfb_DAC_in(minfo, regidx) & 0x40; + } + return r >= (90 * 0x40); + } + /* udelay(1)... but DAC_in is much slower... */ + } + return 0; +} + +static int g450_testpll(const struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll) +{ + return g450_isplllocked(minfo, g450_setpll(minfo, mnp, pll)); +} + +static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) { + switch (pll) { + case M_SYSTEM_PLL: + hw->DACclk[3] = mnp >> 16; + hw->DACclk[4] = mnp >> 8; + hw->DACclk[5] = mnp; + break; + } +} + +void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll) +{ + if (g450_cmppll(minfo, mnp, pll)) { + g450_setpll(minfo, mnp, pll); + } +} + +static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo, + unsigned int pll, + unsigned int *mnparray, + unsigned int mnpcount) +{ + unsigned int found = 0; + unsigned int idx; + unsigned int mnpfound = mnparray[0]; + + for (idx = 0; idx < mnpcount; idx++) { + unsigned int sarray[3]; + unsigned int *sptr; + { + unsigned int mnp; + + sptr = sarray; + mnp = mnparray[idx]; + if (mnp & 0x38) { + *sptr++ = mnp - 8; + } + if ((mnp & 0x38) != 0x38) { + *sptr++ = mnp + 8; + } + *sptr = mnp; + } + while (sptr >= sarray) { + unsigned int mnp = *sptr--; + + if (g450_testpll(minfo, mnp - 0x0300, pll) && + g450_testpll(minfo, mnp + 0x0300, pll) && + g450_testpll(minfo, mnp - 0x0200, pll) && + g450_testpll(minfo, mnp + 0x0200, pll) && + g450_testpll(minfo, mnp - 0x0100, pll) && + g450_testpll(minfo, mnp + 0x0100, pll)) { + if (g450_testpll(minfo, mnp, pll)) { + return mnp; + } + } else if (!found && g450_testpll(minfo, mnp, pll)) { + mnpfound = mnp; + found = 1; + } + } + } + g450_setpll(minfo, mnpfound, pll); + return mnpfound; +} + +static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) { + if (++ci->valid > ARRAY_SIZE(ci->data)) { + ci->valid = ARRAY_SIZE(ci->data); + } + memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS; + ci->data[0].mnp_value = mnp_value; +} + +static int g450_checkcache(struct matrox_fb_info *minfo, + struct matrox_pll_cache *ci, unsigned int mnp_key) +{ + unsigned int i; + + mnp_key &= G450_MNP_FREQBITS; + for (i = 0; i < ci->valid; i++) { + if (ci->data[i].mnp_key == mnp_key) { + unsigned int mnp; + + mnp = ci->data[i].mnp_value; + if (i) { + memmove(ci->data + 1, ci->data, i * sizeof(*ci->data)); + ci->data[0].mnp_key = mnp_key; + ci->data[0].mnp_value = mnp; + } + return mnp; + } + } + return NO_MORE_MNP; +} + +static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll, unsigned int *mnparray, + unsigned int *deltaarray) +{ + unsigned int mnpcount; + unsigned int pixel_vco; + const struct matrox_pll_limits* pi; + struct matrox_pll_cache* ci; + + pixel_vco = 0; + switch (pll) { + case M_PIXEL_PLL_A: + case M_PIXEL_PLL_B: + case M_PIXEL_PLL_C: + { + u_int8_t tmp, xpwrctrl; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + + xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN); + mga_outb(M_SEQ_INDEX, M_SEQ1); + mga_outb(M_SEQ_DATA, mga_inb(M_SEQ_DATA) | M_SEQ1_SCROFF); + tmp = matroxfb_DAC_in(minfo, M1064_XPIXCLKCTRL); + tmp |= M1064_XPIXCLKCTRL_DIS; + if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) { + tmp |= M1064_XPIXCLKCTRL_PLL_UP; + } + matroxfb_DAC_out(minfo, M1064_XPIXCLKCTRL, tmp); + /* DVI PLL preferred for frequencies up to + panel link max, standard PLL otherwise */ + if (fout >= minfo->max_pixel_clock_panellink) + tmp = 0; + else tmp = + M1064_XDVICLKCTRL_DVIDATAPATHSEL | + M1064_XDVICLKCTRL_C1DVICLKSEL | + M1064_XDVICLKCTRL_C1DVICLKEN | + M1064_XDVICLKCTRL_DVILOOPCTL | + M1064_XDVICLKCTRL_P1LOOPBWDTCTL; + /* Setting this breaks PC systems so don't do it */ + /* matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); */ + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, + xpwrctrl); + + matroxfb_DAC_unlock_irqrestore(flags); + } + { + u_int8_t misc; + + misc = mga_inb(M_MISC_REG_READ) & ~0x0C; + switch (pll) { + case M_PIXEL_PLL_A: + break; + case M_PIXEL_PLL_B: + misc |= 0x04; + break; + default: + misc |= 0x0C; + break; + } + mga_outb(M_MISC_REG, misc); + } + pi = &minfo->limits.pixel; + ci = &minfo->cache.pixel; + break; + case M_SYSTEM_PLL: + { + u_int32_t opt; + + pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &opt); + if (!(opt & 0x20)) { + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, opt | 0x20); + } + } + pi = &minfo->limits.system; + ci = &minfo->cache.system; + break; + case M_VIDEO_PLL: + { + u_int8_t tmp; + unsigned int mnp; + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL); + if (!(tmp & 2)) { + matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2); + } + + mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16; + mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8; + pixel_vco = g450_mnp2vco(minfo, mnp); + matroxfb_DAC_unlock_irqrestore(flags); + } + pi = &minfo->limits.video; + ci = &minfo->cache.video; + break; + default: + return -EINVAL; + } + + mnpcount = 0; + { + unsigned int mnp; + unsigned int xvco; + + for (mnp = g450_firstpll(minfo, pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(minfo, pi, &xvco, mnp)) { + unsigned int idx; + unsigned int vco; + unsigned int delta; + + vco = g450_mnp2vco(minfo, mnp); +#if 0 + if (pll == M_VIDEO_PLL) { + unsigned int big, small; + + if (vco < pixel_vco) { + small = vco; + big = pixel_vco; + } else { + small = pixel_vco; + big = vco; + } + while (big > small) { + big >>= 1; + } + if (big == small) { + continue; + } + } +#endif + delta = pll_freq_delta(fout, g450_vco2f(mnp, vco)); + for (idx = mnpcount; idx > 0; idx--) { + /* == is important; due to nextpll algorithm we get + sorted equally good frequencies from lower VCO + frequency to higher - with <= lowest wins, while + with < highest one wins */ + if (delta <= deltaarray[idx-1]) { + /* all else being equal except VCO, + * choose VCO not near (within 1/16th or so) VCOmin + * (freqs near VCOmin aren't as stable) + */ + if (delta == deltaarray[idx-1] + && vco != g450_mnp2vco(minfo, mnparray[idx-1]) + && vco < (pi->vcomin * 17 / 16)) { + break; + } + mnparray[idx] = mnparray[idx-1]; + deltaarray[idx] = deltaarray[idx-1]; + } else { + break; + } + } + mnparray[idx] = mnp; + deltaarray[idx] = delta; + mnpcount++; + } + } + /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */ + if (!mnpcount) { + return -EBUSY; + } + { + unsigned long flags; + unsigned int mnp; + + matroxfb_DAC_lock_irqsave(flags); + mnp = g450_checkcache(minfo, ci, mnparray[0]); + if (mnp != NO_MORE_MNP) { + matroxfb_g450_setpll_cond(minfo, mnp, pll); + } else { + mnp = g450_findworkingpll(minfo, pll, mnparray, mnpcount); + g450_addcache(ci, mnparray[0], mnp); + } + updatehwstate_clk(&minfo->hw, mnp, pll); + matroxfb_DAC_unlock_irqrestore(flags); + return mnp; + } +} + +/* It must be greater than number of possible PLL values. + * Currently there is 5(p) * 10(m) = 50 possible values. */ +#define MNP_TABLE_SIZE 64 + +int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll) +{ + unsigned int* arr; + + arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL); + if (arr) { + int r; + + r = __g450_setclk(minfo, fout, pll, arr, arr + MNP_TABLE_SIZE); + kfree(arr); + return r; + } + return -ENOMEM; +} + +EXPORT_SYMBOL(matroxfb_g450_setclk); +EXPORT_SYMBOL(g450_mnp2f); +EXPORT_SYMBOL(matroxfb_g450_setpll_cond); + +MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec vandrove@vc.cvut.cz"); +MODULE_DESCRIPTION("Matrox G450/G550 PLL driver"); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/g450_pll.h b/drivers/staging/mgakms/g450_pll.h new file mode 100644 index 000000000000..5303336c6547 --- /dev/null +++ b/drivers/staging/mgakms/g450_pll.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __G450_PLL_H__ +#define __G450_PLL_H__ + +#include "matroxfb_base.h" + +int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout, + unsigned int pll); +unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp); +void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp, + unsigned int pll); + +#endif /* __G450_PLL_H__ */ diff --git a/drivers/staging/mgakms/i2c-matroxfb.c b/drivers/staging/mgakms/i2c-matroxfb.c new file mode 100644 index 000000000000..0fb280ead3dc --- /dev/null +++ b/drivers/staging/mgakms/i2c-matroxfb.c @@ -0,0 +1,238 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Version: 1.64 2002/06/10 + * + * See matroxfb_base.c for contributors. + * + */ + +#include "matroxfb_base.h" +#include "matroxfb_maven.h" +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/i2c-algo-bit.h> + +/* MGA-TVO I2C for G200, G400 */ +#define MAT_CLK 0x20 +#define MAT_DATA 0x10 +/* primary head DDC for Mystique(?), G100, G200, G400 */ +#define DDC1_CLK 0x08 +#define DDC1_DATA 0x02 +/* primary head DDC for Millennium, Millennium II */ +#define DDC1B_CLK 0x10 +#define DDC1B_DATA 0x04 +/* secondary head DDC for G400 */ +#define DDC2_CLK 0x04 +#define DDC2_DATA 0x01 + +/******************************************************/ + +struct matroxfb_dh_maven_info { + struct i2c_bit_adapter maven; + struct i2c_bit_adapter ddc1; + struct i2c_bit_adapter ddc2; +}; + +static int matroxfb_read_gpio(struct matrox_fb_info* minfo) { + unsigned long flags; + int v; + + matroxfb_DAC_lock_irqsave(flags); + v = matroxfb_DAC_in(minfo, DAC_XGENIODATA); + matroxfb_DAC_unlock_irqrestore(flags); + return v; +} + +static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { + unsigned long flags; + int v; + + matroxfb_DAC_lock_irqsave(flags); + v = (matroxfb_DAC_in(minfo, DAC_XGENIOCTRL) & mask) | val; + matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, v); + /* We must reset GENIODATA very often... XFree plays with this register */ + matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0x00); + matroxfb_DAC_unlock_irqrestore(flags); +} + +/* software I2C functions */ +static inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) { + if (state) + state = 0; + else + state = mask; + matroxfb_set_gpio(minfo, ~mask, state); +} + +static void matroxfb_gpio_setsda(void* data, int state) { + struct i2c_bit_adapter* b = data; + matroxfb_i2c_set(b->minfo, b->mask.data, state); +} + +static void matroxfb_gpio_setscl(void* data, int state) { + struct i2c_bit_adapter* b = data; + matroxfb_i2c_set(b->minfo, b->mask.clock, state); +} + +static int matroxfb_gpio_getsda(void* data) { + struct i2c_bit_adapter* b = data; + return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0; +} + +static int matroxfb_gpio_getscl(void* data) { + struct i2c_bit_adapter* b = data; + return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0; +} + +static const struct i2c_algo_bit_data matrox_i2c_algo_template = +{ + .setsda = matroxfb_gpio_setsda, + .setscl = matroxfb_gpio_setscl, + .getsda = matroxfb_gpio_getsda, + .getscl = matroxfb_gpio_getscl, + .udelay = 10, + .timeout = 100, +}; + +static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, + unsigned int data, unsigned int clock, const char *name, + int class) +{ + int err; + + b->minfo = minfo; + b->mask.data = data; + b->mask.clock = clock; + b->adapter.owner = THIS_MODULE; + snprintf(b->adapter.name, sizeof(b->adapter.name), name, + minfo->fbcon.node); + i2c_set_adapdata(&b->adapter, b); + b->adapter.class = class; + b->adapter.algo_data = &b->bac; + b->adapter.dev.parent = &minfo->pcidev->dev; + b->bac = matrox_i2c_algo_template; + b->bac.data = b; + err = i2c_bit_add_bus(&b->adapter); + b->initialized = !err; + return err; +} + +static void i2c_bit_bus_del(struct i2c_bit_adapter* b) { + if (b->initialized) { + i2c_del_adapter(&b->adapter); + b->initialized = 0; + } +} + +static inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) { + i2c_bit_bus_del(&minfo2->maven); +} + +static inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) { + i2c_bit_bus_del(&minfo2->ddc1); +} + +static inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) { + i2c_bit_bus_del(&minfo2->ddc2); +} + +static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { + int err; + unsigned long flags; + struct matroxfb_dh_maven_info* m2info; + + m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); + if (!m2info) + return NULL; + + matroxfb_DAC_lock_irqsave(flags); + matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0xFF); + matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, 0x00); + matroxfb_DAC_unlock_irqrestore(flags); + + switch (minfo->chip) { + case MGA_2064: + case MGA_2164: + err = i2c_bus_reg(&m2info->ddc1, minfo, + DDC1B_DATA, DDC1B_CLK, + "DDC:fb%u #0", I2C_CLASS_DDC); + break; + default: + err = i2c_bus_reg(&m2info->ddc1, minfo, + DDC1_DATA, DDC1_CLK, + "DDC:fb%u #0", I2C_CLASS_DDC); + break; + } + if (err) + goto fail_ddc1; + if (minfo->devflags.dualhead) { + err = i2c_bus_reg(&m2info->ddc2, minfo, + DDC2_DATA, DDC2_CLK, + "DDC:fb%u #1", I2C_CLASS_DDC); + if (err == -ENODEV) { + printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); + } else if (err) + printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); + /* Register maven bus even on G450/G550 */ + err = i2c_bus_reg(&m2info->maven, minfo, + MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); + if (err) + printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); + else { + struct i2c_board_info maven_info = { + I2C_BOARD_INFO("maven", 0x1b), + }; + unsigned short const addr_list[2] = { + 0x1b, I2C_CLIENT_END + }; + + i2c_new_probed_device(&m2info->maven.adapter, + &maven_info, addr_list, NULL); + } + } + return m2info; +fail_ddc1:; + kfree(m2info); + printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n"); + return NULL; +} + +static void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) { + struct matroxfb_dh_maven_info* m2info = data; + + i2c_maven_done(m2info); + i2c_ddc2_done(m2info); + i2c_ddc1_done(m2info); + kfree(m2info); +} + +static struct matroxfb_driver i2c_matroxfb = { + .node = LIST_HEAD_INIT(i2c_matroxfb.node), + .name = "i2c-matroxfb", + .probe = i2c_matroxfb_probe, + .remove = i2c_matroxfb_remove, +}; + +static int __init i2c_matroxfb_init(void) { + if (matroxfb_register_driver(&i2c_matroxfb)) { + printk(KERN_ERR "i2c-matroxfb: failed to register driver\n"); + return -ENXIO; + } + return 0; +} + +static void __exit i2c_matroxfb_exit(void) { + matroxfb_unregister_driver(&i2c_matroxfb); +} + +MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec vandrove@vc.cvut.cz"); +MODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards"); + +module_init(i2c_matroxfb_init); +module_exit(i2c_matroxfb_exit); +/* no __setup required */ +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_DAC1064.c b/drivers/staging/mgakms/matroxfb_DAC1064.c new file mode 100644 index 000000000000..b380a393cbc3 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_DAC1064.c @@ -0,0 +1,1113 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.65 2002/08/14 + * + * See matroxfb_base.c for contributors. + * + */ + + +#include "matroxfb_DAC1064.h" +#include "matroxfb_misc.h" +#include "matroxfb_accel.h" +#include "g450_pll.h" +#include <linux/matroxfb.h> + +#ifdef NEED_DAC1064 +#define outDAC1064 matroxfb_DAC_out +#define inDAC1064 matroxfb_DAC_in + +#define DAC1064_OPT_SCLK_PCI 0x00 +#define DAC1064_OPT_SCLK_PLL 0x01 +#define DAC1064_OPT_SCLK_EXT 0x02 +#define DAC1064_OPT_SCLK_MASK 0x03 +#define DAC1064_OPT_GDIV1 0x04 /* maybe it is GDIV2 on G100 ?! */ +#define DAC1064_OPT_GDIV3 0x00 +#define DAC1064_OPT_MDIV1 0x08 +#define DAC1064_OPT_MDIV2 0x00 +#define DAC1064_OPT_RESERVED 0x10 + +static void DAC1064_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, + unsigned int *in, unsigned int *feed, + unsigned int *post) +{ + unsigned int fvco; + unsigned int p; + + DBG(__func__) + + /* only for devices older than G450 */ + + fvco = PLL_calcclock(minfo, freq, fmax, in, feed, &p); + + p = (1 << p) - 1; + if (fvco <= 100000) + ; + else if (fvco <= 140000) + p |= 0x08; + else if (fvco <= 180000) + p |= 0x10; + else + p |= 0x18; + *post = p; +} + +/* they must be in POS order */ +static const unsigned char MGA1064_DAC_regs[] = { + M1064_XCURADDL, M1064_XCURADDH, M1064_XCURCTRL, + M1064_XCURCOL0RED, M1064_XCURCOL0GREEN, M1064_XCURCOL0BLUE, + M1064_XCURCOL1RED, M1064_XCURCOL1GREEN, M1064_XCURCOL1BLUE, + M1064_XCURCOL2RED, M1064_XCURCOL2GREEN, M1064_XCURCOL2BLUE, + DAC1064_XVREFCTRL, M1064_XMULCTRL, M1064_XPIXCLKCTRL, M1064_XGENCTRL, + M1064_XMISCCTRL, + M1064_XGENIOCTRL, M1064_XGENIODATA, M1064_XZOOMCTRL, M1064_XSENSETEST, + M1064_XCRCBITSEL, + M1064_XCOLKEYMASKL, M1064_XCOLKEYMASKH, M1064_XCOLKEYL, M1064_XCOLKEYH }; + +static const unsigned char MGA1064_DAC[] = { + 0x00, 0x00, M1064_XCURCTRL_DIS, + 0x00, 0x00, 0x00, /* black */ + 0xFF, 0xFF, 0xFF, /* white */ + 0xFF, 0x00, 0x00, /* red */ + 0x00, 0, + M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL, + M1064_XGENCTRL_VS_0 | M1064_XGENCTRL_ALPHA_DIS | M1064_XGENCTRL_BLACK_0IRE | M1064_XGENCTRL_NO_SYNC_ON_GREEN, + M1064_XMISCCTRL_DAC_8BIT, + 0x00, 0x00, M1064_XZOOMCTRL_1, M1064_XSENSETEST_BCOMP | M1064_XSENSETEST_GCOMP | M1064_XSENSETEST_RCOMP | M1064_XSENSETEST_PDOWN, + 0x00, + 0x00, 0x00, 0xFF, 0xFF}; + +static void DAC1064_setpclk(struct matrox_fb_info *minfo, unsigned long fout) +{ + unsigned int m, n, p; + + DBG(__func__) + + DAC1064_calcclock(minfo, fout, minfo->max_pixel_clock, &m, &n, &p); + minfo->hw.DACclk[0] = m; + minfo->hw.DACclk[1] = n; + minfo->hw.DACclk[2] = p; +} + +static void DAC1064_setmclk(struct matrox_fb_info *minfo, int oscinfo, + unsigned long fmem) +{ + u_int32_t mx; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + if (minfo->devflags.noinit) { + /* read MCLK and give up... */ + hw->DACclk[3] = inDAC1064(minfo, DAC1064_XSYSPLLM); + hw->DACclk[4] = inDAC1064(minfo, DAC1064_XSYSPLLN); + hw->DACclk[5] = inDAC1064(minfo, DAC1064_XSYSPLLP); + return; + } + mx = hw->MXoptionReg | 0x00000004; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); + mx &= ~0x000000BB; + if (oscinfo & DAC1064_OPT_GDIV1) + mx |= 0x00000008; + if (oscinfo & DAC1064_OPT_MDIV1) + mx |= 0x00000010; + if (oscinfo & DAC1064_OPT_RESERVED) + mx |= 0x00000080; + if ((oscinfo & DAC1064_OPT_SCLK_MASK) == DAC1064_OPT_SCLK_PLL) { + /* select PCI clock until we have setup oscilator... */ + int clk; + unsigned int m, n, p; + + /* powerup system PLL, select PCI clock */ + mx |= 0x00000020; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); + mx &= ~0x00000004; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); + + /* !!! you must not access device if MCLK is not running !!! + Doing so cause immediate PCI lockup :-( Maybe they should + generate ABORT or I/O (parity...) error and Linux should + recover from this... (kill driver/process). But world is not + perfect... */ + /* (bit 2 of PCI_OPTION_REG must be 0... and bits 0,1 must not + select PLL... because of PLL can be stopped at this time) */ + DAC1064_calcclock(minfo, fmem, minfo->max_pixel_clock, &m, &n, &p); + outDAC1064(minfo, DAC1064_XSYSPLLM, hw->DACclk[3] = m); + outDAC1064(minfo, DAC1064_XSYSPLLN, hw->DACclk[4] = n); + outDAC1064(minfo, DAC1064_XSYSPLLP, hw->DACclk[5] = p); + for (clk = 65536; clk; --clk) { + if (inDAC1064(minfo, DAC1064_XSYSPLLSTAT) & 0x40) + break; + } + if (!clk) + printk(KERN_ERR "matroxfb: aiee, SYSPLL not locked\n"); + /* select PLL */ + mx |= 0x00000005; + } else { + /* select specified system clock source */ + mx |= oscinfo & DAC1064_OPT_SCLK_MASK; + } + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); + mx &= ~0x00000004; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mx); + hw->MXoptionReg = mx; +} + +#ifdef CONFIG_FB_MATROX_G +static void g450_set_plls(struct matrox_fb_info *minfo) +{ + u_int32_t c2_ctl; + unsigned int pxc; + struct matrox_hw_state *hw = &minfo->hw; + int pixelmnp; + int videomnp; + + c2_ctl = hw->crtc2.ctl & ~0x4007; /* Clear PLL + enable for CRTC2 */ + c2_ctl |= 0x0001; /* Enable CRTC2 */ + hw->DACreg[POS1064_XPWRCTRL] &= ~0x02; /* Stop VIDEO PLL */ + pixelmnp = minfo->crtc1.mnp; + videomnp = minfo->crtc2.mnp; + if (videomnp < 0) { + c2_ctl &= ~0x0001; /* Disable CRTC2 */ + hw->DACreg[POS1064_XPWRCTRL] &= ~0x10; /* Powerdown CRTC2 */ + } else if (minfo->crtc2.pixclock == minfo->features.pll.ref_freq) { + c2_ctl |= 0x4002; /* Use reference directly */ + } else if (videomnp == pixelmnp) { + c2_ctl |= 0x0004; /* Use pixel PLL */ + } else { + if (0 == ((videomnp ^ pixelmnp) & 0xFFFFFF00)) { + /* PIXEL and VIDEO PLL must not use same frequency. We modify N + of PIXEL PLL in such case because of VIDEO PLL may be source + of TVO clocks, and chroma subcarrier is derived from its + pixel clocks */ + pixelmnp += 0x000100; + } + c2_ctl |= 0x0006; /* Use video PLL */ + hw->DACreg[POS1064_XPWRCTRL] |= 0x02; + + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); + matroxfb_g450_setpll_cond(minfo, videomnp, M_VIDEO_PLL); + } + + hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP; + if (pixelmnp >= 0) { + hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP; + + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); + matroxfb_g450_setpll_cond(minfo, pixelmnp, M_PIXEL_PLL_C); + } + if (c2_ctl != hw->crtc2.ctl) { + hw->crtc2.ctl = c2_ctl; + mga_outl(0x3C10, c2_ctl); + } + + pxc = minfo->crtc1.pixclock; + if (pxc == 0 || minfo->outputs[2].src == MATROXFB_SRC_CRTC2) { + pxc = minfo->crtc2.pixclock; + } + if (minfo->chip == MGA_G550) { + if (pxc < 45000) { + hw->DACreg[POS1064_XPANMODE] = 0x00; /* 0-50 */ + } else if (pxc < 55000) { + hw->DACreg[POS1064_XPANMODE] = 0x08; /* 34-62 */ + } else if (pxc < 70000) { + hw->DACreg[POS1064_XPANMODE] = 0x10; /* 42-78 */ + } else if (pxc < 85000) { + hw->DACreg[POS1064_XPANMODE] = 0x18; /* 62-92 */ + } else if (pxc < 100000) { + hw->DACreg[POS1064_XPANMODE] = 0x20; /* 74-108 */ + } else if (pxc < 115000) { + hw->DACreg[POS1064_XPANMODE] = 0x28; /* 94-122 */ + } else if (pxc < 125000) { + hw->DACreg[POS1064_XPANMODE] = 0x30; /* 108-132 */ + } else { + hw->DACreg[POS1064_XPANMODE] = 0x38; /* 120-168 */ + } + } else { + /* G450 */ + if (pxc < 45000) { + hw->DACreg[POS1064_XPANMODE] = 0x00; /* 0-54 */ + } else if (pxc < 65000) { + hw->DACreg[POS1064_XPANMODE] = 0x08; /* 38-70 */ + } else if (pxc < 85000) { + hw->DACreg[POS1064_XPANMODE] = 0x10; /* 56-96 */ + } else if (pxc < 105000) { + hw->DACreg[POS1064_XPANMODE] = 0x18; /* 80-114 */ + } else if (pxc < 135000) { + hw->DACreg[POS1064_XPANMODE] = 0x20; /* 102-144 */ + } else if (pxc < 160000) { + hw->DACreg[POS1064_XPANMODE] = 0x28; /* 132-166 */ + } else if (pxc < 175000) { + hw->DACreg[POS1064_XPANMODE] = 0x30; /* 154-182 */ + } else { + hw->DACreg[POS1064_XPANMODE] = 0x38; /* 170-204 */ + } + } +} +#endif + +void DAC1064_global_init(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; + + hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; + hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; + hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; +#ifdef CONFIG_FB_MATROX_G + if (minfo->devflags.g450dac) { + hw->DACreg[POS1064_XPWRCTRL] = 0x1F; /* powerup everything */ + hw->DACreg[POS1064_XOUTPUTCONN] = 0x00; /* disable outputs */ + hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; + switch (minfo->outputs[0].src) { + case MATROXFB_SRC_CRTC1: + case MATROXFB_SRC_CRTC2: + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x01; /* enable output; CRTC1/2 selection is in CRTC2 ctl */ + break; + case MATROXFB_SRC_NONE: + hw->DACreg[POS1064_XMISCCTRL] &= ~M1064_XMISCCTRL_DAC_EN; + break; + } + switch (minfo->outputs[1].src) { + case MATROXFB_SRC_CRTC1: + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x04; + break; + case MATROXFB_SRC_CRTC2: + if (minfo->outputs[1].mode == MATROXFB_OUTPUT_MODE_MONITOR) { + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x08; + } else { + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x0C; + } + break; + case MATROXFB_SRC_NONE: + hw->DACreg[POS1064_XPWRCTRL] &= ~0x01; /* Poweroff DAC2 */ + break; + } + switch (minfo->outputs[2].src) { + case MATROXFB_SRC_CRTC1: + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x20; + break; + case MATROXFB_SRC_CRTC2: + hw->DACreg[POS1064_XOUTPUTCONN] |= 0x40; + break; + case MATROXFB_SRC_NONE: +#if 0 + /* HELP! If we boot without DFP connected to DVI, we can + poweroff TMDS. But if we boot with DFP connected, + TMDS generated clocks are used instead of ALL pixclocks + available... If someone knows which register + handles it, please reveal this secret to me... */ + hw->DACreg[POS1064_XPWRCTRL] &= ~0x04; /* Poweroff TMDS */ +#endif + break; + } + /* Now set timming related variables... */ + g450_set_plls(minfo); + } else +#endif + { + if (minfo->outputs[1].src == MATROXFB_SRC_CRTC1) { + hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; + } else if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; + } else if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12; + else + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS; + + if (minfo->outputs[0].src != MATROXFB_SRC_NONE) + hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; + } +} + +void DAC1064_global_restore(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; + + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); + outDAC1064(minfo, M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) { + outDAC1064(minfo, 0x20, 0x04); + outDAC1064(minfo, 0x1F, minfo->devflags.dfp_type); + if (minfo->devflags.g450dac) { + outDAC1064(minfo, M1064_XSYNCCTRL, 0xCC); + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); + outDAC1064(minfo, M1064_XPANMODE, hw->DACreg[POS1064_XPANMODE]); + outDAC1064(minfo, M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]); + } + } +} + +static int DAC1064_init_1(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + memcpy(hw->DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs)); + switch (minfo->fbcon.var.bits_per_pixel) { + /* case 4: not supported by MGA1064 DAC */ + case 8: + hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + break; + case 16: + if (minfo->fbcon.var.green.length == 5) + hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_15BPP_1BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + else + hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_16BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + break; + case 24: + hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_24BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + break; + case 32: + hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_32BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED; + break; + default: + return 1; /* unsupported depth */ + } + hw->DACreg[POS1064_XVREFCTRL] = minfo->features.DAC1064.xvrefctrl; + hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK; + hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN; + hw->DACreg[POS1064_XCURADDL] = 0; + hw->DACreg[POS1064_XCURADDH] = 0; + + DAC1064_global_init(minfo); + return 0; +} + +static int DAC1064_init_2(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + if (minfo->fbcon.var.bits_per_pixel > 16) { /* 256 entries */ + int i; + + for (i = 0; i < 256; i++) { + hw->DACpal[i * 3 + 0] = i; + hw->DACpal[i * 3 + 1] = i; + hw->DACpal[i * 3 + 2] = i; + } + } else if (minfo->fbcon.var.bits_per_pixel > 8) { + if (minfo->fbcon.var.green.length == 5) { /* 0..31, 128..159 */ + int i; + + for (i = 0; i < 32; i++) { + /* with p15 == 0 */ + hw->DACpal[i * 3 + 0] = i << 3; + hw->DACpal[i * 3 + 1] = i << 3; + hw->DACpal[i * 3 + 2] = i << 3; + /* with p15 == 1 */ + hw->DACpal[(i + 128) * 3 + 0] = i << 3; + hw->DACpal[(i + 128) * 3 + 1] = i << 3; + hw->DACpal[(i + 128) * 3 + 2] = i << 3; + } + } else { + int i; + + for (i = 0; i < 64; i++) { /* 0..63 */ + hw->DACpal[i * 3 + 0] = i << 3; + hw->DACpal[i * 3 + 1] = i << 2; + hw->DACpal[i * 3 + 2] = i << 3; + } + } + } else { + memset(hw->DACpal, 0, 768); + } + return 0; +} + +static void DAC1064_restore_1(struct matrox_fb_info *minfo) +{ + struct matrox_hw_state *hw = &minfo->hw; + + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + if ((inDAC1064(minfo, DAC1064_XSYSPLLM) != hw->DACclk[3]) || + (inDAC1064(minfo, DAC1064_XSYSPLLN) != hw->DACclk[4]) || + (inDAC1064(minfo, DAC1064_XSYSPLLP) != hw->DACclk[5])) { + outDAC1064(minfo, DAC1064_XSYSPLLM, hw->DACclk[3]); + outDAC1064(minfo, DAC1064_XSYSPLLN, hw->DACclk[4]); + outDAC1064(minfo, DAC1064_XSYSPLLP, hw->DACclk[5]); + } + { + unsigned int i; + + for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { + if ((i != POS1064_XPIXCLKCTRL) && (i != POS1064_XMISCCTRL)) + outDAC1064(minfo, MGA1064_DAC_regs[i], hw->DACreg[i]); + } + } + + DAC1064_global_restore(minfo); + + CRITEND +}; + +static void DAC1064_restore_2(struct matrox_fb_info *minfo) +{ +#ifdef DEBUG + unsigned int i; +#endif + + DBG(__func__) + +#ifdef DEBUG + dprintk(KERN_DEBUG "DAC1064regs "); + for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) { + dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], minfo->hw.DACreg[i]); + if ((i & 0x7) == 0x7) dprintk(KERN_DEBUG "continuing... "); + } + dprintk(KERN_DEBUG "DAC1064clk "); + for (i = 0; i < 6; i++) + dprintk("C%02X=%02X ", i, minfo->hw.DACclk[i]); + dprintk("\n"); +#endif +} + +static int m1064_compute(void* out, struct my_timming* m) { +#define minfo ((struct matrox_fb_info*)out) + { + int i; + int tmout; + CRITFLAGS + + DAC1064_setpclk(minfo, m->pixclock); + + CRITBEGIN + + for (i = 0; i < 3; i++) + outDAC1064(minfo, M1064_XPIXPLLCM + i, minfo->hw.DACclk[i]); + for (tmout = 500000; tmout; tmout--) { + if (inDAC1064(minfo, M1064_XPIXPLLSTAT) & 0x40) + break; + udelay(10); + } + + CRITEND + + if (!tmout) + printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + } +#undef minfo + return 0; +} + +static struct matrox_altout m1064 = { + .name = "Primary output", + .compute = m1064_compute, +}; + +#ifdef CONFIG_FB_MATROX_G +static int g450_compute(void* out, struct my_timming* m) { +#define minfo ((struct matrox_fb_info*)out) + if (m->mnp < 0) { + m->mnp = matroxfb_g450_setclk(minfo, m->pixclock, (m->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + if (m->mnp >= 0) { + m->pixclock = g450_mnp2f(minfo, m->mnp); + } + } +#undef minfo + return 0; +} + +static struct matrox_altout g450out = { + .name = "Primary output", + .compute = g450_compute, +}; +#endif + +#endif /* NEED_DAC1064 */ + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +static int MGA1064_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + if (DAC1064_init_1(minfo, m)) return 1; + if (matroxfb_vgaHWinit(minfo, m)) return 1; + + hw->MiscOutReg = 0xCB; + if (m->sync & FB_SYNC_HOR_HIGH_ACT) + hw->MiscOutReg &= ~0x40; + if (m->sync & FB_SYNC_VERT_HIGH_ACT) + hw->MiscOutReg &= ~0x80; + if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ + hw->CRTCEXT[3] |= 0x40; + + if (DAC1064_init_2(minfo, m)) return 1; + return 0; +} +#endif + +#ifdef CONFIG_FB_MATROX_G +static int MGAG100_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + if (DAC1064_init_1(minfo, m)) return 1; + hw->MXoptionReg &= ~0x2000; + if (matroxfb_vgaHWinit(minfo, m)) return 1; + + hw->MiscOutReg = 0xEF; + if (m->sync & FB_SYNC_HOR_HIGH_ACT) + hw->MiscOutReg &= ~0x40; + if (m->sync & FB_SYNC_VERT_HIGH_ACT) + hw->MiscOutReg &= ~0x80; + if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */ + hw->CRTCEXT[3] |= 0x40; + + if (DAC1064_init_2(minfo, m)) return 1; + return 0; +} +#endif /* G */ + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +static void MGA1064_ramdac_init(struct matrox_fb_info *minfo) +{ + + DBG(__func__) + + /* minfo->features.DAC1064.vco_freq_min = 120000; */ + minfo->features.pll.vco_freq_min = 62000; + minfo->features.pll.ref_freq = 14318; + minfo->features.pll.feed_div_min = 100; + minfo->features.pll.feed_div_max = 127; + minfo->features.pll.in_div_min = 1; + minfo->features.pll.in_div_max = 31; + minfo->features.pll.post_shift_max = 3; + minfo->features.DAC1064.xvrefctrl = DAC1064_XVREFCTRL_EXTERNAL; + /* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */ + DAC1064_setmclk(minfo, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); +} +#endif + +#ifdef CONFIG_FB_MATROX_G +/* BIOS environ */ +static int x7AF4 = 0x10; /* flags, maybe 0x10 = SDRAM, 0x00 = SGRAM??? */ + /* G100 wants 0x10, G200 SGRAM does not care... */ +#if 0 +static int def50 = 0; /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */ +#endif + +static void MGAG100_progPixClock(const struct matrox_fb_info *minfo, int flags, + int m, int n, int p) +{ + int reg; + int selClk; + int clk; + + DBG(__func__) + + outDAC1064(minfo, M1064_XPIXCLKCTRL, inDAC1064(minfo, M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS | + M1064_XPIXCLKCTRL_PLL_UP); + switch (flags & 3) { + case 0: reg = M1064_XPIXPLLAM; break; + case 1: reg = M1064_XPIXPLLBM; break; + default: reg = M1064_XPIXPLLCM; break; + } + outDAC1064(minfo, reg++, m); + outDAC1064(minfo, reg++, n); + outDAC1064(minfo, reg, p); + selClk = mga_inb(M_MISC_REG_READ) & ~0xC; + /* there should be flags & 0x03 & case 0/1/else */ + /* and we should first select source and after that we should wait for PLL */ + /* and we are waiting for PLL with oscilator disabled... Is it right? */ + switch (flags & 0x03) { + case 0x00: break; + case 0x01: selClk |= 4; break; + default: selClk |= 0x0C; break; + } + mga_outb(M_MISC_REG, selClk); + for (clk = 500000; clk; clk--) { + if (inDAC1064(minfo, M1064_XPIXPLLSTAT) & 0x40) + break; + udelay(10); + } + if (!clk) + printk(KERN_ERR "matroxfb: Pixel PLL%c not locked after usual time\n", (reg-M1064_XPIXPLLAM-2)/4 + 'A'); + selClk = inDAC1064(minfo, M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK; + switch (flags & 0x0C) { + case 0x00: selClk |= M1064_XPIXCLKCTRL_SRC_PCI; break; + case 0x04: selClk |= M1064_XPIXCLKCTRL_SRC_PLL; break; + default: selClk |= M1064_XPIXCLKCTRL_SRC_EXT; break; + } + outDAC1064(minfo, M1064_XPIXCLKCTRL, selClk); + outDAC1064(minfo, M1064_XPIXCLKCTRL, inDAC1064(minfo, M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS); +} + +static void MGAG100_setPixClock(const struct matrox_fb_info *minfo, int flags, + int freq) +{ + unsigned int m, n, p; + + DBG(__func__) + + DAC1064_calcclock(minfo, freq, minfo->max_pixel_clock, &m, &n, &p); + MGAG100_progPixClock(minfo, flags, m, n, p); +} +#endif + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +static int MGA1064_preinit(struct matrox_fb_info *minfo) +{ + static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960, + 1024, 1152, 1280, 1600, 1664, 1920, + 2048, 0}; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + /* minfo->capable.cfb4 = 0; ... preinitialized by 0 */ + minfo->capable.text = 1; + minfo->capable.vxres = vxres_mystique; + + minfo->outputs[0].output = &m1064; + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].data = minfo; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; + + if (minfo->devflags.noinit) + return 0; /* do not modify settings */ + hw->MXoptionReg &= 0xC0000100; + hw->MXoptionReg |= 0x00094E20; + if (minfo->devflags.novga) + hw->MXoptionReg &= ~0x00000100; + if (minfo->devflags.nobios) + hw->MXoptionReg &= ~0x40000000; + if (minfo->devflags.nopciretry) + hw->MXoptionReg |= 0x20000000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + mga_setr(M_SEQ_INDEX, 0x01, 0x20); + mga_outl(M_CTLWTST, 0x00000000); + udelay(200); + mga_outl(M_MACCESS, 0x00008000); + udelay(100); + mga_outl(M_MACCESS, 0x0000C000); + return 0; +} + +static void MGA1064_reset(struct matrox_fb_info *minfo) +{ + + DBG(__func__); + + MGA1064_ramdac_init(minfo); +} +#endif + +#ifdef CONFIG_FB_MATROX_G +static void g450_mclk_init(struct matrox_fb_info *minfo) +{ + /* switch all clocks to PCI source */ + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); + pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3 & ~0x00300C03); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + if (((minfo->values.reg.opt3 & 0x000003) == 0x000003) || + ((minfo->values.reg.opt3 & 0x000C00) == 0x000C00) || + ((minfo->values.reg.opt3 & 0x300000) == 0x300000)) { + matroxfb_g450_setclk(minfo, minfo->values.pll.video, M_VIDEO_PLL); + } else { + unsigned long flags; + unsigned int pwr; + + matroxfb_DAC_lock_irqsave(flags); + pwr = inDAC1064(minfo, M1064_XPWRCTRL) & ~0x02; + outDAC1064(minfo, M1064_XPWRCTRL, pwr); + matroxfb_DAC_unlock_irqrestore(flags); + } + matroxfb_g450_setclk(minfo, minfo->values.pll.system, M_SYSTEM_PLL); + + /* switch clocks to their real PLL source(s) */ + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); + pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + +} + +static void g450_memory_init(struct matrox_fb_info *minfo) +{ + /* disable memory refresh */ + minfo->hw.MXoptionReg &= ~0x001F8000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + /* set memory interface parameters */ + minfo->hw.MXoptionReg &= ~0x00207E00; + minfo->hw.MXoptionReg |= 0x00207E00 & minfo->values.reg.opt; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, minfo->values.reg.opt2); + + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + + /* first set up memory interface with disabled memory interface clocks */ + pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc & ~0x80000000U); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); + mga_outl(M_MACCESS, minfo->values.reg.maccess); + /* start memory clocks */ + pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc | 0x80000000U); + + udelay(200); + + if (minfo->values.memory.ddr && (!minfo->values.memory.emrswen || !minfo->values.memory.dll)) { + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk & ~0x1000); + } + mga_outl(M_MACCESS, minfo->values.reg.maccess | 0x8000); + + udelay(200); + + minfo->hw.MXoptionReg |= 0x001F8000 & minfo->values.reg.opt; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + /* value is written to memory chips only if old != new */ + mga_outl(M_PLNWT, 0); + mga_outl(M_PLNWT, ~0); + + if (minfo->values.reg.mctlwtst != minfo->values.reg.mctlwtst_core) { + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst_core); + } + +} + +static void g450_preinit(struct matrox_fb_info *minfo) +{ + u_int32_t c2ctl; + u_int8_t curctl; + u_int8_t c1ctl; + + /* minfo->hw.MXoptionReg = minfo->values.reg.opt; */ + minfo->hw.MXoptionReg &= 0xC0000100; + minfo->hw.MXoptionReg |= 0x00000020; + if (minfo->devflags.novga) + minfo->hw.MXoptionReg &= ~0x00000100; + if (minfo->devflags.nobios) + minfo->hw.MXoptionReg &= ~0x40000000; + if (minfo->devflags.nopciretry) + minfo->hw.MXoptionReg |= 0x20000000; + minfo->hw.MXoptionReg |= minfo->values.reg.opt & 0x03400040; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + /* Init system clocks */ + + /* stop crtc2 */ + c2ctl = mga_inl(M_C2CTL); + mga_outl(M_C2CTL, c2ctl & ~1); + /* stop cursor */ + curctl = inDAC1064(minfo, M1064_XCURCTRL); + outDAC1064(minfo, M1064_XCURCTRL, 0); + /* stop crtc1 */ + c1ctl = mga_readr(M_SEQ_INDEX, 1); + mga_setr(M_SEQ_INDEX, 1, c1ctl | 0x20); + + g450_mclk_init(minfo); + g450_memory_init(minfo); + + /* set legacy VGA clock sources for DOSEmu or VMware... */ + matroxfb_g450_setclk(minfo, 25175, M_PIXEL_PLL_A); + matroxfb_g450_setclk(minfo, 28322, M_PIXEL_PLL_B); + + /* restore crtc1 */ + mga_setr(M_SEQ_INDEX, 1, c1ctl); + + /* restore cursor */ + outDAC1064(minfo, M1064_XCURCTRL, curctl); + + /* restore crtc2 */ + mga_outl(M_C2CTL, c2ctl); + + return; +} + +static int MGAG100_preinit(struct matrox_fb_info *minfo) +{ + static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960, + 1024, 1152, 1280, 1600, 1664, 1920, + 2048, 0}; + struct matrox_hw_state *hw = &minfo->hw; + + u_int32_t reg50; +#if 0 + u_int32_t q; +#endif + + DBG(__func__) + + /* there are some instabilities if in_div > 19 && vco < 61000 */ + if (minfo->devflags.g450dac) { + minfo->features.pll.vco_freq_min = 130000; /* my sample: >118 */ + } else { + minfo->features.pll.vco_freq_min = 62000; + } + if (!minfo->features.pll.ref_freq) { + minfo->features.pll.ref_freq = 27000; + } + minfo->features.pll.feed_div_min = 7; + minfo->features.pll.feed_div_max = 127; + minfo->features.pll.in_div_min = 1; + minfo->features.pll.in_div_max = 31; + minfo->features.pll.post_shift_max = 3; + minfo->features.DAC1064.xvrefctrl = DAC1064_XVREFCTRL_G100_DEFAULT; + /* minfo->capable.cfb4 = 0; ... preinitialized by 0 */ + minfo->capable.text = 1; + minfo->capable.vxres = vxres_g100; + minfo->capable.plnwt = minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG100 + ? minfo->devflags.sgram : 1; + + if (minfo->devflags.g450dac) { + minfo->outputs[0].output = &g450out; + } else { + minfo->outputs[0].output = &m1064; + } + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].data = minfo; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; + + if (minfo->devflags.g450dac) { + /* we must do this always, BIOS does not do it for us + and accelerator dies without it */ + mga_outl(0x1C0C, 0); + } + if (minfo->devflags.noinit) + return 0; + if (minfo->devflags.g450dac) { + g450_preinit(minfo); + return 0; + } + hw->MXoptionReg &= 0xC0000100; + hw->MXoptionReg |= 0x00000020; + if (minfo->devflags.novga) + hw->MXoptionReg &= ~0x00000100; + if (minfo->devflags.nobios) + hw->MXoptionReg &= ~0x40000000; + if (minfo->devflags.nopciretry) + hw->MXoptionReg |= 0x20000000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + DAC1064_setmclk(minfo, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333); + + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG100) { + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); + reg50 &= ~0x3000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); + + hw->MXoptionReg |= 0x1080; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + udelay(100); + mga_outb(0x1C05, 0x00); + mga_outb(0x1C05, 0x80); + udelay(100); + mga_outb(0x1C05, 0x40); + mga_outb(0x1C05, 0xC0); + udelay(100); + reg50 &= ~0xFF; + reg50 |= 0x07; + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); + /* it should help with G100 */ + mga_outb(M_GRAPHICS_INDEX, 6); + mga_outb(M_GRAPHICS_DATA, (mga_inb(M_GRAPHICS_DATA) & 3) | 4); + mga_setr(M_EXTVGA_INDEX, 0x03, 0x81); + mga_setr(M_EXTVGA_INDEX, 0x04, 0x00); + mga_writeb(minfo->video.vbase, 0x0000, 0xAA); + mga_writeb(minfo->video.vbase, 0x0800, 0x55); + mga_writeb(minfo->video.vbase, 0x4000, 0x55); +#if 0 + if (mga_readb(minfo->video.vbase, 0x0000) != 0xAA) { + hw->MXoptionReg &= ~0x1000; + } +#endif + hw->MXoptionReg |= 0x00078020; + } else if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG200) { + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); + reg50 &= ~0x3000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); + + if (minfo->devflags.memtype == -1) + hw->MXoptionReg |= minfo->values.reg.opt & 0x1C00; + else + hw->MXoptionReg |= (minfo->devflags.memtype & 7) << 10; + if (minfo->devflags.sgram) + hw->MXoptionReg |= 0x4000; + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); + udelay(200); + mga_outl(M_MACCESS, 0x00000000); + mga_outl(M_MACCESS, 0x00008000); + udelay(100); + mga_outw(M_MEMRDBK, minfo->values.reg.memrdbk); + hw->MXoptionReg |= 0x00078020; + } else { + pci_read_config_dword(minfo->pcidev, PCI_OPTION2_REG, ®50); + reg50 &= ~0x00000100; + reg50 |= 0x00000000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, reg50); + + if (minfo->devflags.memtype == -1) + hw->MXoptionReg |= minfo->values.reg.opt & 0x1C00; + else + hw->MXoptionReg |= (minfo->devflags.memtype & 7) << 10; + if (minfo->devflags.sgram) + hw->MXoptionReg |= 0x4000; + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); + udelay(200); + mga_outl(M_MACCESS, 0x00000000); + mga_outl(M_MACCESS, 0x00008000); + udelay(100); + mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); + hw->MXoptionReg |= 0x00040020; + } + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + return 0; +} + +static void MGAG100_reset(struct matrox_fb_info *minfo) +{ + u_int8_t b; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + { +#ifdef G100_BROKEN_IBM_82351 + u_int32_t d; + + find 1014/22 (IBM/82351); /* if found and bridging Matrox, do some strange stuff */ + pci_read_config_byte(ibm, PCI_SECONDARY_BUS, &b); + if (b == minfo->pcidev->bus->number) { + pci_write_config_byte(ibm, PCI_COMMAND+1, 0); /* disable back-to-back & SERR */ + pci_write_config_byte(ibm, 0x41, 0xF4); /* ??? */ + pci_write_config_byte(ibm, PCI_IO_BASE, 0xF0); /* ??? */ + pci_write_config_byte(ibm, PCI_IO_LIMIT, 0x00); /* ??? */ + } +#endif + if (!minfo->devflags.noinit) { + if (x7AF4 & 8) { + hw->MXoptionReg |= 0x40; /* FIXME... */ + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + } + mga_setr(M_EXTVGA_INDEX, 0x06, 0x00); + } + } + if (minfo->devflags.g450dac) { + /* either leave MCLK as is... or they were set in preinit */ + hw->DACclk[3] = inDAC1064(minfo, DAC1064_XSYSPLLM); + hw->DACclk[4] = inDAC1064(minfo, DAC1064_XSYSPLLN); + hw->DACclk[5] = inDAC1064(minfo, DAC1064_XSYSPLLP); + } else { + DAC1064_setmclk(minfo, DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333); + } + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) { + if (minfo->devflags.dfp_type == -1) { + minfo->devflags.dfp_type = inDAC1064(minfo, 0x1F); + } + } + if (minfo->devflags.noinit) + return; + if (minfo->devflags.g450dac) { + } else { + MGAG100_setPixClock(minfo, 4, 25175); + MGAG100_setPixClock(minfo, 5, 28322); + if (x7AF4 & 0x10) { + b = inDAC1064(minfo, M1064_XGENIODATA) & ~1; + outDAC1064(minfo, M1064_XGENIODATA, b); + b = inDAC1064(minfo, M1064_XGENIOCTRL) | 1; + outDAC1064(minfo, M1064_XGENIOCTRL, b); + } + } +} +#endif + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +static void MGA1064_restore(struct matrox_fb_info *minfo) +{ + int i; + struct matrox_hw_state *hw = &minfo->hw; + + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + mga_outb(M_IEN, 0x00); + mga_outb(M_CACHEFLUSH, 0x00); + + CRITEND + + DAC1064_restore_1(minfo); + matroxfb_vgaHWrestore(minfo); + minfo->crtc1.panpos = -1; + for (i = 0; i < 6; i++) + mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); + DAC1064_restore_2(minfo); +} +#endif + +#ifdef CONFIG_FB_MATROX_G +static void MGAG100_restore(struct matrox_fb_info *minfo) +{ + int i; + struct matrox_hw_state *hw = &minfo->hw; + + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + CRITEND + + DAC1064_restore_1(minfo); + matroxfb_vgaHWrestore(minfo); + if (minfo->devflags.support32MB) + mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]); + minfo->crtc1.panpos = -1; + for (i = 0; i < 6; i++) + mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); + DAC1064_restore_2(minfo); +} +#endif + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +struct matrox_switch matrox_mystique = { + .preinit = MGA1064_preinit, + .reset = MGA1064_reset, + .init = MGA1064_init, + .restore = MGA1064_restore, +}; +EXPORT_SYMBOL(matrox_mystique); +#endif + +#ifdef CONFIG_FB_MATROX_G +struct matrox_switch matrox_G100 = { + .preinit = MGAG100_preinit, + .reset = MGAG100_reset, + .init = MGAG100_init, + .restore = MGAG100_restore, +}; +EXPORT_SYMBOL(matrox_G100); +#endif + +#ifdef NEED_DAC1064 +EXPORT_SYMBOL(DAC1064_global_init); +EXPORT_SYMBOL(DAC1064_global_restore); +#endif +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_DAC1064.h b/drivers/staging/mgakms/matroxfb_DAC1064.h new file mode 100644 index 000000000000..3b2a6fd35fff --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_DAC1064.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_DAC1064_H__ +#define __MATROXFB_DAC1064_H__ + + +#include "matroxfb_base.h" + +#ifdef CONFIG_FB_MATROX_MYSTIQUE +extern struct matrox_switch matrox_mystique; +#endif +#ifdef CONFIG_FB_MATROX_G +extern struct matrox_switch matrox_G100; +#endif +#ifdef NEED_DAC1064 +void DAC1064_global_init(struct matrox_fb_info *minfo); +void DAC1064_global_restore(struct matrox_fb_info *minfo); +#endif + +#define M1064_INDEX 0x00 +#define M1064_PALWRADD 0x00 +#define M1064_PALDATA 0x01 +#define M1064_PIXRDMSK 0x02 +#define M1064_PALRDADD 0x03 +#define M1064_X_DATAREG 0x0A +#define M1064_CURPOSXL 0x0C /* can be accessed as DWORD */ +#define M1064_CURPOSXH 0x0D +#define M1064_CURPOSYL 0x0E +#define M1064_CURPOSYH 0x0F + +#define M1064_XCURADDL 0x04 +#define M1064_XCURADDH 0x05 +#define M1064_XCURCTRL 0x06 +#define M1064_XCURCTRL_DIS 0x00 /* transparent, transparent, transparent, transparent */ +#define M1064_XCURCTRL_3COLOR 0x01 /* transparent, 0, 1, 2 */ +#define M1064_XCURCTRL_XGA 0x02 /* 0, 1, transparent, complement */ +#define M1064_XCURCTRL_XWIN 0x03 /* transparent, transparent, 0, 1 */ + /* drive DVI by standard(0)/DVI(1) PLL */ + /* if set(1), C?DVICLKEN and C?DVICLKSEL must be set(1) */ +#define M1064_XDVICLKCTRL_DVIDATAPATHSEL 0x01 + /* drive CRTC1 by standard(0)/DVI(1) PLL */ +#define M1064_XDVICLKCTRL_C1DVICLKSEL 0x02 + /* drive CRTC2 by standard(0)/DVI(1) PLL */ +#define M1064_XDVICLKCTRL_C2DVICLKSEL 0x04 + /* pixel clock allowed to(0)/blocked from(1) driving CRTC1 */ +#define M1064_XDVICLKCTRL_C1DVICLKEN 0x08 + /* DVI PLL loop filter bandwidth selection bits */ +#define M1064_XDVICLKCTRL_DVILOOPCTL 0x30 + /* CRTC2 pixel clock allowed to(0)/blocked from(1) driving CRTC2 */ +#define M1064_XDVICLKCTRL_C2DVICLKEN 0x40 + /* P1PLL loop filter bandwidth selection */ +#define M1064_XDVICLKCTRL_P1LOOPBWDTCTL 0x80 +#define M1064_XCURCOL0RED 0x08 +#define M1064_XCURCOL0GREEN 0x09 +#define M1064_XCURCOL0BLUE 0x0A +#define M1064_XCURCOL1RED 0x0C +#define M1064_XCURCOL1GREEN 0x0D +#define M1064_XCURCOL1BLUE 0x0E +#define M1064_XDVICLKCTRL 0x0F +#define M1064_XCURCOL2RED 0x10 +#define M1064_XCURCOL2GREEN 0x11 +#define M1064_XCURCOL2BLUE 0x12 +#define DAC1064_XVREFCTRL 0x18 +#define DAC1064_XVREFCTRL_INTERNAL 0x3F +#define DAC1064_XVREFCTRL_EXTERNAL 0x00 +#define DAC1064_XVREFCTRL_G100_DEFAULT 0x03 +#define M1064_XMULCTRL 0x19 +#define M1064_XMULCTRL_DEPTH_8BPP 0x00 /* 8 bpp paletized */ +#define M1064_XMULCTRL_DEPTH_15BPP_1BPP 0x01 /* 15 bpp paletized + 1 bpp overlay */ +#define M1064_XMULCTRL_DEPTH_16BPP 0x02 /* 16 bpp paletized */ +#define M1064_XMULCTRL_DEPTH_24BPP 0x03 /* 24 bpp paletized */ +#define M1064_XMULCTRL_DEPTH_24BPP_8BPP 0x04 /* 24 bpp direct + 8 bpp overlay paletized */ +#define M1064_XMULCTRL_2G8V16 0x05 /* 15 bpp video direct, half xres, 8bpp paletized */ +#define M1064_XMULCTRL_G16V16 0x06 /* 15 bpp video, 15bpp graphics, one of them paletized */ +#define M1064_XMULCTRL_DEPTH_32BPP 0x07 /* 24 bpp paletized + 8 bpp unused */ +#define M1064_XMULCTRL_GRAPHICS_PALETIZED 0x00 +#define M1064_XMULCTRL_VIDEO_PALETIZED 0x08 +#define M1064_XPIXCLKCTRL 0x1A +#define M1064_XPIXCLKCTRL_SRC_PCI 0x00 +#define M1064_XPIXCLKCTRL_SRC_PLL 0x01 +#define M1064_XPIXCLKCTRL_SRC_EXT 0x02 +#define M1064_XPIXCLKCTRL_SRC_SYS 0x03 /* G200/G400 */ +#define M1064_XPIXCLKCTRL_SRC_PLL2 0x03 /* G450 */ +#define M1064_XPIXCLKCTRL_SRC_MASK 0x03 +#define M1064_XPIXCLKCTRL_EN 0x00 +#define M1064_XPIXCLKCTRL_DIS 0x04 +#define M1064_XPIXCLKCTRL_PLL_DOWN 0x00 +#define M1064_XPIXCLKCTRL_PLL_UP 0x08 +#define M1064_XGENCTRL 0x1D +#define M1064_XGENCTRL_VS_0 0x00 +#define M1064_XGENCTRL_VS_1 0x01 +#define M1064_XGENCTRL_ALPHA_DIS 0x00 +#define M1064_XGENCTRL_ALPHA_EN 0x02 +#define M1064_XGENCTRL_BLACK_0IRE 0x00 +#define M1064_XGENCTRL_BLACK_75IRE 0x10 +#define M1064_XGENCTRL_SYNC_ON_GREEN 0x00 +#define M1064_XGENCTRL_NO_SYNC_ON_GREEN 0x20 +#define M1064_XGENCTRL_SYNC_ON_GREEN_MASK 0x20 +#define M1064_XMISCCTRL 0x1E +#define M1064_XMISCCTRL_DAC_DIS 0x00 +#define M1064_XMISCCTRL_DAC_EN 0x01 +#define M1064_XMISCCTRL_MFC_VGA 0x00 +#define M1064_XMISCCTRL_MFC_MAFC 0x02 +#define M1064_XMISCCTRL_MFC_DIS 0x06 +#define GX00_XMISCCTRL_MFC_MAFC 0x02 +#define GX00_XMISCCTRL_MFC_PANELLINK 0x04 +#define GX00_XMISCCTRL_MFC_DIS 0x06 +#define GX00_XMISCCTRL_MFC_MASK 0x06 +#define M1064_XMISCCTRL_DAC_6BIT 0x00 +#define M1064_XMISCCTRL_DAC_8BIT 0x08 +#define M1064_XMISCCTRL_DAC_WIDTHMASK 0x08 +#define M1064_XMISCCTRL_LUT_DIS 0x00 +#define M1064_XMISCCTRL_LUT_EN 0x10 +#define G400_XMISCCTRL_VDO_MAFC12 0x00 +#define G400_XMISCCTRL_VDO_BYPASS656 0x40 +#define G400_XMISCCTRL_VDO_C2_MAFC12 0x80 +#define G400_XMISCCTRL_VDO_C2_BYPASS656 0xC0 +#define G400_XMISCCTRL_VDO_MASK 0xE0 +#define M1064_XGENIOCTRL 0x2A +#define M1064_XGENIODATA 0x2B +#define DAC1064_XSYSPLLM 0x2C +#define DAC1064_XSYSPLLN 0x2D +#define DAC1064_XSYSPLLP 0x2E +#define DAC1064_XSYSPLLSTAT 0x2F +#define M1064_XZOOMCTRL 0x38 +#define M1064_XZOOMCTRL_1 0x00 +#define M1064_XZOOMCTRL_2 0x01 +#define M1064_XZOOMCTRL_4 0x03 +#define M1064_XSENSETEST 0x3A +#define M1064_XSENSETEST_BCOMP 0x01 +#define M1064_XSENSETEST_GCOMP 0x02 +#define M1064_XSENSETEST_RCOMP 0x04 +#define M1064_XSENSETEST_PDOWN 0x00 +#define M1064_XSENSETEST_PUP 0x80 +#define M1064_XCRCREML 0x3C +#define M1064_XCRCREMH 0x3D +#define M1064_XCRCBITSEL 0x3E +#define M1064_XCOLKEYMASKL 0x40 +#define M1064_XCOLKEYMASKH 0x41 +#define M1064_XCOLKEYL 0x42 +#define M1064_XCOLKEYH 0x43 +#define M1064_XPIXPLLAM 0x44 +#define M1064_XPIXPLLAN 0x45 +#define M1064_XPIXPLLAP 0x46 +#define M1064_XPIXPLLBM 0x48 +#define M1064_XPIXPLLBN 0x49 +#define M1064_XPIXPLLBP 0x4A +#define M1064_XPIXPLLCM 0x4C +#define M1064_XPIXPLLCN 0x4D +#define M1064_XPIXPLLCP 0x4E +#define M1064_XPIXPLLSTAT 0x4F + +#define M1064_XTVO_IDX 0x87 +#define M1064_XTVO_DATA 0x88 + +#define M1064_XOUTPUTCONN 0x8A +#define M1064_XSYNCCTRL 0x8B +#define M1064_XVIDPLLSTAT 0x8C +#define M1064_XVIDPLLP 0x8D +#define M1064_XVIDPLLM 0x8E +#define M1064_XVIDPLLN 0x8F + +#define M1064_XPWRCTRL 0xA0 +#define M1064_XPWRCTRL_PANELPDN 0x04 + +#define M1064_XPANMODE 0xA2 + +enum POS1064 { + POS1064_XCURADDL=0, POS1064_XCURADDH, POS1064_XCURCTRL, + POS1064_XCURCOL0RED, POS1064_XCURCOL0GREEN, POS1064_XCURCOL0BLUE, + POS1064_XCURCOL1RED, POS1064_XCURCOL1GREEN, POS1064_XCURCOL1BLUE, + POS1064_XCURCOL2RED, POS1064_XCURCOL2GREEN, POS1064_XCURCOL2BLUE, + POS1064_XVREFCTRL, POS1064_XMULCTRL, POS1064_XPIXCLKCTRL, POS1064_XGENCTRL, + POS1064_XMISCCTRL, + POS1064_XGENIOCTRL, POS1064_XGENIODATA, POS1064_XZOOMCTRL, POS1064_XSENSETEST, + POS1064_XCRCBITSEL, + POS1064_XCOLKEYMASKL, POS1064_XCOLKEYMASKH, POS1064_XCOLKEYL, POS1064_XCOLKEYH, + POS1064_XOUTPUTCONN, POS1064_XPANMODE, POS1064_XPWRCTRL }; + + +#endif /* __MATROXFB_DAC1064_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_Ti3026.c b/drivers/staging/mgakms/matroxfb_Ti3026.c new file mode 100644 index 000000000000..9ff9be85759e --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_Ti3026.c @@ -0,0 +1,748 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.65 2002/08/14 + * + * MTRR stuff: 1998 Tom Rini trini@kernel.crashing.org + * + * Contributors: "menion?" menion@mindless.com + * Betatesting, fixes, ideas + * + * "Kurt Garloff" garloff@suse.de + * Betatesting, fixes, ideas, videomodes, videomodes timmings + * + * "Tom Rini" trini@kernel.crashing.org + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas + * + * "Bibek Sahu" scorpio@dodds.net + * Access device through readb|w|l and write b|w|l + * Extensive debugging stuff + * + * "Daniel Haun" haund@usa.net + * Testing, hardware cursor fixes + * + * "Scott Wood" sawst46+@pitt.edu + * Fixes + * + * "Gerd Knorr" kraxel@goldbach.isdn.cs.tu-berlin.de + * Betatesting + * + * "Kelly French" targon@hazmat.com + * "Fernando Herrera" fherrera@eurielec.etsit.upm.es + * Betatesting, bug reporting + * + * "Pablo Bianucci" pbian@pccp.com.ar + * Fixes, ideas, betatesting + * + * "Inaky Perez Gonzalez" inaky@peloncho.fis.ucm.es + * Fixes, enhandcements, ideas, betatesting + * + * "Ryuichi Oikawa" roikawa@rr.iiij4u.or.jp + * PPC betatesting, PPC support, backward compatibility + * + * "Paul Womar" Paul@pwomar.demon.co.uk + * "Owen Waller" O.Waller@ee.qub.ac.uk + * PPC betatesting + * + * "Thomas Pornin" pornin@bolet.ens.fr + * Alpha betatesting + * + * "Pieter van Leuven" pvl@iae.nl + * "Ulf Jaenicke-Roessler" ujr@physik.phy.tu-dresden.de + * G100 testing + * + * "H. Peter Arvin" hpa@transmeta.com + * Ideas + * + * "Cort Dougan" cort@cs.nmt.edu + * CHRP fixes and PReP cleanup + * + * "Mark Vojkovich" mvojkovi@ucsd.edu + * G400 support + * + * (following author is not in any relation with this code, but his code + * is included in this driver) + * + * Based on framebuffer driver for VBE 2.0 compliant graphic boards + * (c) 1998 Gerd Knorr kraxel@cs.tu-berlin.de + * + * (following author is not in any relation with this code, but his ideas + * were used when writing this driver) + * + * FreeVBE/AF (Matrox), "Shawn Hargreaves" shawn@talula.demon.co.uk + * + */ + + +#include "matroxfb_Ti3026.h" +#include "matroxfb_misc.h" +#include "matroxfb_accel.h" +#include <linux/matroxfb.h> + +#ifdef CONFIG_FB_MATROX_MILLENIUM +#define outTi3026 matroxfb_DAC_out +#define inTi3026 matroxfb_DAC_in + +#define TVP3026_INDEX 0x00 +#define TVP3026_PALWRADD 0x00 +#define TVP3026_PALDATA 0x01 +#define TVP3026_PIXRDMSK 0x02 +#define TVP3026_PALRDADD 0x03 +#define TVP3026_CURCOLWRADD 0x04 +#define TVP3026_CLOVERSCAN 0x00 +#define TVP3026_CLCOLOR0 0x01 +#define TVP3026_CLCOLOR1 0x02 +#define TVP3026_CLCOLOR2 0x03 +#define TVP3026_CURCOLDATA 0x05 +#define TVP3026_CURCOLRDADD 0x07 +#define TVP3026_CURCTRL 0x09 +#define TVP3026_X_DATAREG 0x0A +#define TVP3026_CURRAMDATA 0x0B +#define TVP3026_CURPOSXL 0x0C +#define TVP3026_CURPOSXH 0x0D +#define TVP3026_CURPOSYL 0x0E +#define TVP3026_CURPOSYH 0x0F + +#define TVP3026_XSILICONREV 0x01 +#define TVP3026_XCURCTRL 0x06 +#define TVP3026_XCURCTRL_DIS 0x00 /* transparent, transparent, transparent, transparent */ +#define TVP3026_XCURCTRL_3COLOR 0x01 /* transparent, 0, 1, 2 */ +#define TVP3026_XCURCTRL_XGA 0x02 /* 0, 1, transparent, complement */ +#define TVP3026_XCURCTRL_XWIN 0x03 /* transparent, transparent, 0, 1 */ +#define TVP3026_XCURCTRL_BLANK2048 0x00 +#define TVP3026_XCURCTRL_BLANK4096 0x10 +#define TVP3026_XCURCTRL_INTERLACED 0x20 +#define TVP3026_XCURCTRL_ODD 0x00 /* ext.signal ODD/\EVEN */ +#define TVP3026_XCURCTRL_EVEN 0x40 /* ext.signal EVEN/\ODD */ +#define TVP3026_XCURCTRL_INDIRECT 0x00 +#define TVP3026_XCURCTRL_DIRECT 0x80 +#define TVP3026_XLATCHCTRL 0x0F +#define TVP3026_XLATCHCTRL_1_1 0x06 +#define TVP3026_XLATCHCTRL_2_1 0x07 +#define TVP3026_XLATCHCTRL_4_1 0x06 +#define TVP3026_XLATCHCTRL_8_1 0x06 +#define TVP3026_XLATCHCTRL_16_1 0x06 +#define TVP3026A_XLATCHCTRL_4_3 0x06 /* ??? do not understand... but it works... !!! */ +#define TVP3026A_XLATCHCTRL_8_3 0x07 +#define TVP3026B_XLATCHCTRL_4_3 0x08 +#define TVP3026B_XLATCHCTRL_8_3 0x06 /* ??? do not understand... but it works... !!! */ +#define TVP3026_XTRUECOLORCTRL 0x18 +#define TVP3026_XTRUECOLORCTRL_VRAM_SHIFT_ACCEL 0x00 +#define TVP3026_XTRUECOLORCTRL_VRAM_SHIFT_TVP 0x20 +#define TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR 0x80 +#define TVP3026_XTRUECOLORCTRL_TRUECOLOR 0x40 /* paletized */ +#define TVP3026_XTRUECOLORCTRL_DIRECTCOLOR 0x00 +#define TVP3026_XTRUECOLORCTRL_24_ALTERNATE 0x08 /* 5:4/5:2 instead of 4:3/8:3 */ +#define TVP3026_XTRUECOLORCTRL_RGB_888 0x16 /* 4:3/8:3 (or 5:4/5:2) */ +#define TVP3026_XTRUECOLORCTRL_BGR_888 0x17 +#define TVP3026_XTRUECOLORCTRL_ORGB_8888 0x06 +#define TVP3026_XTRUECOLORCTRL_BGRO_8888 0x07 +#define TVP3026_XTRUECOLORCTRL_RGB_565 0x05 +#define TVP3026_XTRUECOLORCTRL_ORGB_1555 0x04 +#define TVP3026_XTRUECOLORCTRL_RGB_664 0x03 +#define TVP3026_XTRUECOLORCTRL_RGBO_4444 0x01 +#define TVP3026_XMUXCTRL 0x19 +#define TVP3026_XMUXCTRL_MEMORY_8BIT 0x01 /* - */ +#define TVP3026_XMUXCTRL_MEMORY_16BIT 0x02 /* - */ +#define TVP3026_XMUXCTRL_MEMORY_32BIT 0x03 /* 2MB RAM, 512K * 4 */ +#define TVP3026_XMUXCTRL_MEMORY_64BIT 0x04 /* >2MB RAM, 512K * 8 & more */ +#define TVP3026_XMUXCTRL_PIXEL_4BIT 0x40 /* L0,H0,L1,H1... */ +#define TVP3026_XMUXCTRL_PIXEL_4BIT_SWAPPED 0x60 /* H0,L0,H1,L1... */ +#define TVP3026_XMUXCTRL_PIXEL_8BIT 0x48 +#define TVP3026_XMUXCTRL_PIXEL_16BIT 0x50 +#define TVP3026_XMUXCTRL_PIXEL_32BIT 0x58 +#define TVP3026_XMUXCTRL_VGA 0x98 /* VGA MEMORY, 8BIT PIXEL */ +#define TVP3026_XCLKCTRL 0x1A +#define TVP3026_XCLKCTRL_DIV1 0x00 +#define TVP3026_XCLKCTRL_DIV2 0x10 +#define TVP3026_XCLKCTRL_DIV4 0x20 +#define TVP3026_XCLKCTRL_DIV8 0x30 +#define TVP3026_XCLKCTRL_DIV16 0x40 +#define TVP3026_XCLKCTRL_DIV32 0x50 +#define TVP3026_XCLKCTRL_DIV64 0x60 +#define TVP3026_XCLKCTRL_CLKSTOPPED 0x70 +#define TVP3026_XCLKCTRL_SRC_CLK0 0x00 +#define TVP3026_XCLKCTRL_SRC_CLK1 0x01 +#define TVP3026_XCLKCTRL_SRC_CLK2 0x02 /* CLK2 is TTL source*/ +#define TVP3026_XCLKCTRL_SRC_NCLK2 0x03 /* not CLK2 is TTL source */ +#define TVP3026_XCLKCTRL_SRC_ECLK2 0x04 /* CLK2 and not CLK2 is ECL source */ +#define TVP3026_XCLKCTRL_SRC_PLL 0x05 +#define TVP3026_XCLKCTRL_SRC_DIS 0x06 /* disable & poweroff internal clock */ +#define TVP3026_XCLKCTRL_SRC_CLK0VGA 0x07 +#define TVP3026_XPALETTEPAGE 0x1C +#define TVP3026_XGENCTRL 0x1D +#define TVP3026_XGENCTRL_HSYNC_POS 0x00 +#define TVP3026_XGENCTRL_HSYNC_NEG 0x01 +#define TVP3026_XGENCTRL_VSYNC_POS 0x00 +#define TVP3026_XGENCTRL_VSYNC_NEG 0x02 +#define TVP3026_XGENCTRL_LITTLE_ENDIAN 0x00 +#define TVP3026_XGENCTRL_BIG_ENDIAN 0x08 +#define TVP3026_XGENCTRL_BLACK_0IRE 0x00 +#define TVP3026_XGENCTRL_BLACK_75IRE 0x10 +#define TVP3026_XGENCTRL_NO_SYNC_ON_GREEN 0x00 +#define TVP3026_XGENCTRL_SYNC_ON_GREEN 0x20 +#define TVP3026_XGENCTRL_OVERSCAN_DIS 0x00 +#define TVP3026_XGENCTRL_OVERSCAN_EN 0x40 +#define TVP3026_XMISCCTRL 0x1E +#define TVP3026_XMISCCTRL_DAC_PUP 0x00 +#define TVP3026_XMISCCTRL_DAC_PDOWN 0x01 +#define TVP3026_XMISCCTRL_DAC_EXT 0x00 /* or 8, bit 3 is ignored */ +#define TVP3026_XMISCCTRL_DAC_6BIT 0x04 +#define TVP3026_XMISCCTRL_DAC_8BIT 0x0C +#define TVP3026_XMISCCTRL_PSEL_DIS 0x00 +#define TVP3026_XMISCCTRL_PSEL_EN 0x10 +#define TVP3026_XMISCCTRL_PSEL_LOW 0x00 /* PSEL high selects directcolor */ +#define TVP3026_XMISCCTRL_PSEL_HIGH 0x20 /* PSEL high selects truecolor or pseudocolor */ +#define TVP3026_XGENIOCTRL 0x2A +#define TVP3026_XGENIODATA 0x2B +#define TVP3026_XPLLADDR 0x2C +#define TVP3026_XPLLADDR_X(LOOP,MCLK,PIX) (((LOOP)<<4) | ((MCLK)<<2) | (PIX)) +#define TVP3026_XPLLDATA_N 0x00 +#define TVP3026_XPLLDATA_M 0x01 +#define TVP3026_XPLLDATA_P 0x02 +#define TVP3026_XPLLDATA_STAT 0x03 +#define TVP3026_XPIXPLLDATA 0x2D +#define TVP3026_XMEMPLLDATA 0x2E +#define TVP3026_XLOOPPLLDATA 0x2F +#define TVP3026_XCOLKEYOVRMIN 0x30 +#define TVP3026_XCOLKEYOVRMAX 0x31 +#define TVP3026_XCOLKEYREDMIN 0x32 +#define TVP3026_XCOLKEYREDMAX 0x33 +#define TVP3026_XCOLKEYGREENMIN 0x34 +#define TVP3026_XCOLKEYGREENMAX 0x35 +#define TVP3026_XCOLKEYBLUEMIN 0x36 +#define TVP3026_XCOLKEYBLUEMAX 0x37 +#define TVP3026_XCOLKEYCTRL 0x38 +#define TVP3026_XCOLKEYCTRL_OVR_EN 0x01 +#define TVP3026_XCOLKEYCTRL_RED_EN 0x02 +#define TVP3026_XCOLKEYCTRL_GREEN_EN 0x04 +#define TVP3026_XCOLKEYCTRL_BLUE_EN 0x08 +#define TVP3026_XCOLKEYCTRL_NEGATE 0x10 +#define TVP3026_XCOLKEYCTRL_ZOOM1 0x00 +#define TVP3026_XCOLKEYCTRL_ZOOM2 0x20 +#define TVP3026_XCOLKEYCTRL_ZOOM4 0x40 +#define TVP3026_XCOLKEYCTRL_ZOOM8 0x60 +#define TVP3026_XCOLKEYCTRL_ZOOM16 0x80 +#define TVP3026_XCOLKEYCTRL_ZOOM32 0xA0 +#define TVP3026_XMEMPLLCTRL 0x39 +#define TVP3026_XMEMPLLCTRL_DIV(X) (((X)-1)>>1) /* 2,4,6,8,10,12,14,16, division applied to LOOP PLL after divide by 2^P */ +#define TVP3026_XMEMPLLCTRL_STROBEMKC4 0x08 +#define TVP3026_XMEMPLLCTRL_MCLK_DOTCLOCK 0x00 /* MKC4 */ +#define TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL 0x10 /* MKC4 */ +#define TVP3026_XMEMPLLCTRL_RCLK_PIXPLL 0x00 +#define TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL 0x20 +#define TVP3026_XMEMPLLCTRL_RCLK_DOTDIVN 0x40 /* dot clock divided by loop pclk N prescaler */ +#define TVP3026_XSENSETEST 0x3A +#define TVP3026_XTESTMODEDATA 0x3B +#define TVP3026_XCRCREML 0x3C +#define TVP3026_XCRCREMH 0x3D +#define TVP3026_XCRCBITSEL 0x3E +#define TVP3026_XID 0x3F + +static const unsigned char DACseq[] = +{ TVP3026_XLATCHCTRL, TVP3026_XTRUECOLORCTRL, + TVP3026_XMUXCTRL, TVP3026_XCLKCTRL, + TVP3026_XPALETTEPAGE, + TVP3026_XGENCTRL, + TVP3026_XMISCCTRL, + TVP3026_XGENIOCTRL, + TVP3026_XGENIODATA, + TVP3026_XCOLKEYOVRMIN, TVP3026_XCOLKEYOVRMAX, TVP3026_XCOLKEYREDMIN, TVP3026_XCOLKEYREDMAX, + TVP3026_XCOLKEYGREENMIN, TVP3026_XCOLKEYGREENMAX, TVP3026_XCOLKEYBLUEMIN, TVP3026_XCOLKEYBLUEMAX, + TVP3026_XCOLKEYCTRL, + TVP3026_XMEMPLLCTRL, TVP3026_XSENSETEST, TVP3026_XCURCTRL }; + +#define POS3026_XLATCHCTRL 0 +#define POS3026_XTRUECOLORCTRL 1 +#define POS3026_XMUXCTRL 2 +#define POS3026_XCLKCTRL 3 +#define POS3026_XGENCTRL 5 +#define POS3026_XMISCCTRL 6 +#define POS3026_XMEMPLLCTRL 18 +#define POS3026_XCURCTRL 20 + +static const unsigned char MGADACbpp32[] = +{ TVP3026_XLATCHCTRL_2_1, TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_8888, + 0x00, TVP3026_XCLKCTRL_DIV1 | TVP3026_XCLKCTRL_SRC_PLL, + 0x00, + TVP3026_XGENCTRL_HSYNC_POS | TVP3026_XGENCTRL_VSYNC_POS | TVP3026_XGENCTRL_LITTLE_ENDIAN | TVP3026_XGENCTRL_BLACK_0IRE | TVP3026_XGENCTRL_NO_SYNC_ON_GREEN | TVP3026_XGENCTRL_OVERSCAN_DIS, + TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_HIGH, + 0x00, + 0x1E, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + TVP3026_XCOLKEYCTRL_ZOOM1, + 0x00, 0x00, TVP3026_XCURCTRL_DIS }; + +static int Ti3026_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, int *in, + int *feed, int *post) +{ + unsigned int fvco; + unsigned int lin, lfeed, lpost; + + DBG(__func__) + + fvco = PLL_calcclock(minfo, freq, fmax, &lin, &lfeed, &lpost); + fvco >>= (*post = lpost); + *in = 64 - lin; + *feed = 64 - lfeed; + return fvco; +} + +static int Ti3026_setpclk(struct matrox_fb_info *minfo, int clk) +{ + unsigned int f_pll; + unsigned int pixfeed, pixin, pixpost; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + f_pll = Ti3026_calcclock(minfo, clk, minfo->max_pixel_clock, &pixin, &pixfeed, &pixpost); + + hw->DACclk[0] = pixin | 0xC0; + hw->DACclk[1] = pixfeed; + hw->DACclk[2] = pixpost | 0xB0; + + { + unsigned int loopfeed, loopin, looppost, loopdiv, z; + unsigned int Bpp; + + Bpp = minfo->curr.final_bppShift; + + if (minfo->fbcon.var.bits_per_pixel == 24) { + loopfeed = 3; /* set lm to any possible value */ + loopin = 3 * 32 / Bpp; + } else { + loopfeed = 4; + loopin = 4 * 32 / Bpp; + } + z = (110000 * loopin) / (f_pll * loopfeed); + loopdiv = 0; /* div 2 */ + if (z < 2) + looppost = 0; + else if (z < 4) + looppost = 1; + else if (z < 8) + looppost = 2; + else { + looppost = 3; + loopdiv = z/16; + } + if (minfo->fbcon.var.bits_per_pixel == 24) { + hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; + hw->DACclk[4] = (65 - loopfeed) | 0x80; + if (minfo->accel.ramdac_rev > 0x20) { + if (isInterleave(minfo)) + hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_8_3; + else { + hw->DACclk[4] &= ~0xC0; + hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_4_3; + } + } else { + if (isInterleave(minfo)) + ; /* default... */ + else { + hw->DACclk[4] ^= 0xC0; /* change from 0x80 to 0x40 */ + hw->DACreg[POS3026_XLATCHCTRL] = TVP3026A_XLATCHCTRL_4_3; + } + } + hw->DACclk[5] = looppost | 0xF8; + if (minfo->devflags.mga_24bpp_fix) + hw->DACclk[5] ^= 0x40; + } else { + hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0; + hw->DACclk[4] = 65 - loopfeed; + hw->DACclk[5] = looppost | 0xF0; + } + hw->DACreg[POS3026_XMEMPLLCTRL] = loopdiv | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL; + } + return 0; +} + +static int Ti3026_init(struct matrox_fb_info *minfo, struct my_timming *m) +{ + u_int8_t muxctrl = isInterleave(minfo) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + memcpy(hw->DACreg, MGADACbpp32, sizeof(MGADACbpp32)); + switch (minfo->fbcon.var.bits_per_pixel) { + case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */ + hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; + hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_4BIT; + hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV8; + hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; + break; + case 8: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; /* or _4_1, they are same */ + hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; + hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_8BIT; + hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; + hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW; + break; + case 16: + /* XLATCHCTRL should be _4_1 / _2_1... Why is not? (_2_1 is used every time) */ + hw->DACreg[POS3026_XTRUECOLORCTRL] = (minfo->fbcon.var.green.length == 5) ? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565); + hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_16BIT; + hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV2; + break; + case 24: + /* XLATCHCTRL is: for (A) use _4_3 (?_8_3 is same? TBD), for (B) it is set in setpclk */ + hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_888; + hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; + hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4; + break; + case 32: + /* XLATCHCTRL should be _2_1 / _1_1... Why is not? (_2_1 is used every time) */ + hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT; + break; + default: + return 1; /* TODO: failed */ + } + if (matroxfb_vgaHWinit(minfo, m)) return 1; + + /* set SYNC */ + hw->MiscOutReg = 0xCB; + if (m->sync & FB_SYNC_HOR_HIGH_ACT) + hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_HSYNC_NEG; + if (m->sync & FB_SYNC_VERT_HIGH_ACT) + hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_VSYNC_NEG; + if (m->sync & FB_SYNC_ON_GREEN) + hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_SYNC_ON_GREEN; + + /* set DELAY */ + if (minfo->video.len < 0x400000) + hw->CRTCEXT[3] |= 0x08; + else if (minfo->video.len > 0x400000) + hw->CRTCEXT[3] |= 0x10; + + /* set HWCURSOR */ + if (m->interlaced) { + hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_INTERLACED; + } + if (m->HTotal >= 1536) + hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_BLANK4096; + + /* set interleaving */ + hw->MXoptionReg &= ~0x00001000; + if (isInterleave(minfo)) hw->MXoptionReg |= 0x00001000; + + /* set DAC */ + Ti3026_setpclk(minfo, m->pixclock); + return 0; +} + +static void ti3026_setMCLK(struct matrox_fb_info *minfo, int fout) +{ + unsigned int f_pll; + unsigned int pclk_m, pclk_n, pclk_p; + unsigned int mclk_m, mclk_n, mclk_p; + unsigned int rfhcnt, mclk_ctl; + int tmout; + + DBG(__func__) + + f_pll = Ti3026_calcclock(minfo, fout, minfo->max_pixel_clock, &mclk_n, &mclk_m, &mclk_p); + + /* save pclk */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + pclk_n = inTi3026(minfo, TVP3026_XPIXPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFD); + pclk_m = inTi3026(minfo, TVP3026_XPIXPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + pclk_p = inTi3026(minfo, TVP3026_XPIXPLLDATA); + + /* stop pclk */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); + + /* set pclk to new mclk */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_n | 0xC0); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_m); + outTi3026(minfo, TVP3026_XPIXPLLDATA, mclk_p | 0xB0); + + /* wait for PLL to lock */ + for (tmout = 500000; tmout; tmout--) { + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) + break; + udelay(10); + } + if (!tmout) + printk(KERN_ERR "matroxfb: Temporary pixel PLL not locked after 5 secs\n"); + + /* output pclk on mclk pin */ + mclk_ctl = inTi3026(minfo, TVP3026_XMEMPLLCTRL); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4); + + /* stop MCLK */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFB); + outTi3026(minfo, TVP3026_XMEMPLLDATA, 0x00); + + /* set mclk to new freq */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xF3); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_n | 0xC0); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_m); + outTi3026(minfo, TVP3026_XMEMPLLDATA, mclk_p | 0xB0); + + /* wait for PLL to lock */ + for (tmout = 500000; tmout; tmout--) { + if (inTi3026(minfo, TVP3026_XMEMPLLDATA) & 0x40) + break; + udelay(10); + } + if (!tmout) + printk(KERN_ERR "matroxfb: Memory PLL not locked after 5 secs\n"); + + f_pll = f_pll * 333 / (10000 << mclk_p); + if (isMilleniumII(minfo)) { + rfhcnt = (f_pll - 128) / 256; + if (rfhcnt > 15) + rfhcnt = 15; + } else { + rfhcnt = (f_pll - 64) / 128; + if (rfhcnt > 15) + rfhcnt = 0; + } + minfo->hw.MXoptionReg = (minfo->hw.MXoptionReg & ~0x000F0000) | (rfhcnt << 16); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); + + /* output MCLK to MCLK pin */ + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + outTi3026(minfo, TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4); + + /* stop PCLK */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFE); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); + + /* restore pclk */ + outTi3026(minfo, TVP3026_XPLLADDR, 0xFC); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_n); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_m); + outTi3026(minfo, TVP3026_XPIXPLLDATA, pclk_p); + + /* wait for PLL to lock */ + for (tmout = 500000; tmout; tmout--) { + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) + break; + udelay(10); + } + if (!tmout) + printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); +} + +static void ti3026_ramdac_init(struct matrox_fb_info *minfo) +{ + DBG(__func__) + + minfo->features.pll.vco_freq_min = 110000; + minfo->features.pll.ref_freq = 114545; + minfo->features.pll.feed_div_min = 2; + minfo->features.pll.feed_div_max = 24; + minfo->features.pll.in_div_min = 2; + minfo->features.pll.in_div_max = 63; + minfo->features.pll.post_shift_max = 3; + if (minfo->devflags.noinit) + return; + ti3026_setMCLK(minfo, 60000); +} + +static void Ti3026_restore(struct matrox_fb_info *minfo) +{ + int i; + unsigned char progdac[6]; + struct matrox_hw_state *hw = &minfo->hw; + CRITFLAGS + + DBG(__func__) + +#ifdef DEBUG + dprintk(KERN_INFO "EXTVGA regs: "); + for (i = 0; i < 6; i++) + dprintk("%02X:", hw->CRTCEXT[i]); + dprintk("\n"); +#endif + + CRITBEGIN + + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + + CRITEND + + matroxfb_vgaHWrestore(minfo); + + CRITBEGIN + + minfo->crtc1.panpos = -1; + for (i = 0; i < 6; i++) + mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); + + for (i = 0; i < 21; i++) { + outTi3026(minfo, DACseq[i], hw->DACreg[i]); + } + + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); + progdac[0] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[3] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0x15); + progdac[1] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[4] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + progdac[2] = inTi3026(minfo, TVP3026_XPIXPLLDATA); + progdac[5] = inTi3026(minfo, TVP3026_XLOOPPLLDATA); + + CRITEND + if (memcmp(hw->DACclk, progdac, 6)) { + /* agrhh... setting up PLL is very slow on Millennium... */ + /* Mystique PLL is locked in few ms, but Millennium PLL lock takes about 0.15 s... */ + /* Maybe even we should call schedule() ? */ + + CRITBEGIN + outTi3026(minfo, TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]); + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + outTi3026(minfo, TVP3026_XLOOPPLLDATA, 0); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0); + + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); + for (i = 0; i < 3; i++) + outTi3026(minfo, TVP3026_XPIXPLLDATA, hw->DACclk[i]); + /* wait for PLL only if PLL clock requested (always for PowerMode, never for VGA) */ + if (hw->MiscOutReg & 0x08) { + int tmout; + outTi3026(minfo, TVP3026_XPLLADDR, 0x3F); + for (tmout = 500000; tmout; --tmout) { + if (inTi3026(minfo, TVP3026_XPIXPLLDATA) & 0x40) + break; + udelay(10); + } + + CRITEND + + if (!tmout) + printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n"); + else + dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout); + CRITBEGIN + } + outTi3026(minfo, TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]); + outTi3026(minfo, TVP3026_XPLLADDR, 0x00); + for (i = 3; i < 6; i++) + outTi3026(minfo, TVP3026_XLOOPPLLDATA, hw->DACclk[i]); + CRITEND + if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) { + int tmout; + + CRITBEGIN + outTi3026(minfo, TVP3026_XPLLADDR, 0x3F); + for (tmout = 500000; tmout; --tmout) { + if (inTi3026(minfo, TVP3026_XLOOPPLLDATA) & 0x40) + break; + udelay(10); + } + CRITEND + if (!tmout) + printk(KERN_ERR "matroxfb: Loop PLL not locked after 5 secs\n"); + else + dprintk(KERN_INFO "LoopPLL: %d\n", 500000-tmout); + } + } + +#ifdef DEBUG + dprintk(KERN_DEBUG "3026DACregs "); + for (i = 0; i < 21; i++) { + dprintk("R%02X=%02X ", DACseq[i], hw->DACreg[i]); + if ((i & 0x7) == 0x7) dprintk(KERN_DEBUG "continuing... "); + } + dprintk(KERN_DEBUG "DACclk "); + for (i = 0; i < 6; i++) + dprintk("C%02X=%02X ", i, hw->DACclk[i]); + dprintk("\n"); +#endif +} + +static void Ti3026_reset(struct matrox_fb_info *minfo) +{ + DBG(__func__) + + ti3026_ramdac_init(minfo); +} + +static struct matrox_altout ti3026_output = { + .name = "Primary output", +}; + +static int Ti3026_preinit(struct matrox_fb_info *minfo) +{ + static const int vxres_mill2[] = { 512, 640, 768, 800, 832, 960, + 1024, 1152, 1280, 1600, 1664, 1920, + 2048, 0}; + static const int vxres_mill1[] = { 640, 768, 800, 960, + 1024, 1152, 1280, 1600, 1920, + 2048, 0}; + struct matrox_hw_state *hw = &minfo->hw; + + DBG(__func__) + + minfo->millenium = 1; + minfo->milleniumII = (minfo->pcidev->device != PCI_DEVICE_ID_MATROX_MIL); + minfo->capable.cfb4 = 1; + minfo->capable.text = 1; /* isMilleniumII(minfo); */ + minfo->capable.vxres = isMilleniumII(minfo) ? vxres_mill2 : vxres_mill1; + + minfo->outputs[0].data = minfo; + minfo->outputs[0].output = &ti3026_output; + minfo->outputs[0].src = minfo->outputs[0].default_src; + minfo->outputs[0].mode = MATROXFB_OUTPUT_MODE_MONITOR; + + if (minfo->devflags.noinit) + return 0; + /* preserve VGA I/O, BIOS and PPC */ + hw->MXoptionReg &= 0xC0000100; + hw->MXoptionReg |= 0x002C0000; + if (minfo->devflags.novga) + hw->MXoptionReg &= ~0x00000100; + if (minfo->devflags.nobios) + hw->MXoptionReg &= ~0x40000000; + if (minfo->devflags.nopciretry) + hw->MXoptionReg |= 0x20000000; + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, hw->MXoptionReg); + + minfo->accel.ramdac_rev = inTi3026(minfo, TVP3026_XSILICONREV); + + outTi3026(minfo, TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED); + outTi3026(minfo, TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR); + outTi3026(minfo, TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA); + + outTi3026(minfo, TVP3026_XPLLADDR, 0x2A); + outTi3026(minfo, TVP3026_XLOOPPLLDATA, 0x00); + outTi3026(minfo, TVP3026_XPIXPLLDATA, 0x00); + + mga_outb(M_MISC_REG, 0x67); + + outTi3026(minfo, TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL); + + mga_outl(M_RESET, 1); + udelay(250); + mga_outl(M_RESET, 0); + udelay(250); + mga_outl(M_MACCESS, 0x00008000); + udelay(10); + return 0; +} + +struct matrox_switch matrox_millennium = { + .preinit = Ti3026_preinit, + .reset = Ti3026_reset, + .init = Ti3026_init, + .restore = Ti3026_restore +}; +EXPORT_SYMBOL(matrox_millennium); +#endif +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_Ti3026.h b/drivers/staging/mgakms/matroxfb_Ti3026.h new file mode 100644 index 000000000000..faee149d0ba0 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_Ti3026.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_TI3026_H__ +#define __MATROXFB_TI3026_H__ + + +#include "matroxfb_base.h" + +#ifdef CONFIG_FB_MATROX_MILLENIUM +extern struct matrox_switch matrox_millennium; +#endif + +#endif /* __MATROXFB_TI3026_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_accel.c b/drivers/staging/mgakms/matroxfb_accel.c new file mode 100644 index 000000000000..0d5cb85d071a --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_accel.c @@ -0,0 +1,519 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Version: 1.65 2002/08/14 + * + * MTRR stuff: 1998 Tom Rini trini@kernel.crashing.org + * + * Contributors: "menion?" menion@mindless.com + * Betatesting, fixes, ideas + * + * "Kurt Garloff" garloff@suse.de + * Betatesting, fixes, ideas, videomodes, videomodes timmings + * + * "Tom Rini" trini@kernel.crashing.org + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas + * + * "Bibek Sahu" scorpio@dodds.net + * Access device through readb|w|l and write b|w|l + * Extensive debugging stuff + * + * "Daniel Haun" haund@usa.net + * Testing, hardware cursor fixes + * + * "Scott Wood" sawst46+@pitt.edu + * Fixes + * + * "Gerd Knorr" kraxel@goldbach.isdn.cs.tu-berlin.de + * Betatesting + * + * "Kelly French" targon@hazmat.com + * "Fernando Herrera" fherrera@eurielec.etsit.upm.es + * Betatesting, bug reporting + * + * "Pablo Bianucci" pbian@pccp.com.ar + * Fixes, ideas, betatesting + * + * "Inaky Perez Gonzalez" inaky@peloncho.fis.ucm.es + * Fixes, enhandcements, ideas, betatesting + * + * "Ryuichi Oikawa" roikawa@rr.iiij4u.or.jp + * PPC betatesting, PPC support, backward compatibility + * + * "Paul Womar" Paul@pwomar.demon.co.uk + * "Owen Waller" O.Waller@ee.qub.ac.uk + * PPC betatesting + * + * "Thomas Pornin" pornin@bolet.ens.fr + * Alpha betatesting + * + * "Pieter van Leuven" pvl@iae.nl + * "Ulf Jaenicke-Roessler" ujr@physik.phy.tu-dresden.de + * G100 testing + * + * "H. Peter Arvin" hpa@transmeta.com + * Ideas + * + * "Cort Dougan" cort@cs.nmt.edu + * CHRP fixes and PReP cleanup + * + * "Mark Vojkovich" mvojkovi@ucsd.edu + * G400 support + * + * (following author is not in any relation with this code, but his code + * is included in this driver) + * + * Based on framebuffer driver for VBE 2.0 compliant graphic boards + * (c) 1998 Gerd Knorr kraxel@cs.tu-berlin.de + * + * (following author is not in any relation with this code, but his ideas + * were used when writing this driver) + * + * FreeVBE/AF (Matrox), "Shawn Hargreaves" shawn@talula.demon.co.uk + * + */ + +#include "matroxfb_accel.h" +#include "matroxfb_DAC1064.h" +#include "matroxfb_Ti3026.h" +#include "matroxfb_misc.h" + +#define curr_ydstorg(x) ((x)->curr.ydstorg.pixels) + +#define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) + +static inline void matrox_cfb4_pal(u_int32_t* pal) { + unsigned int i; + + for (i = 0; i < 16; i++) { + pal[i] = i * 0x11111111U; + } +} + +static inline void matrox_cfb8_pal(u_int32_t* pal) { + unsigned int i; + + for (i = 0; i < 16; i++) { + pal[i] = i * 0x01010101U; + } +} + +static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area); +static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect); +static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image); +static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect); +static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area); + +void matrox_cfbX_init(struct matrox_fb_info *minfo) +{ + u_int32_t maccess; + u_int32_t mpitch; + u_int32_t mopmode; + int accel; + + DBG(__func__) + + mpitch = minfo->fbcon.var.xres_virtual; + + minfo->fbops.fb_copyarea = cfb_copyarea; + minfo->fbops.fb_fillrect = cfb_fillrect; + minfo->fbops.fb_imageblit = cfb_imageblit; + minfo->fbops.fb_cursor = NULL; + + accel = (minfo->fbcon.var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; + + switch (minfo->fbcon.var.bits_per_pixel) { + case 4: maccess = 0x00000000; /* accelerate as 8bpp video */ + mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */ + mopmode = M_OPMODE_4BPP; + matrox_cfb4_pal(minfo->cmap); + if (accel && !(mpitch & 1)) { + minfo->fbops.fb_copyarea = matroxfb_cfb4_copyarea; + minfo->fbops.fb_fillrect = matroxfb_cfb4_fillrect; + } + break; + case 8: maccess = 0x00000000; + mopmode = M_OPMODE_8BPP; + matrox_cfb8_pal(minfo->cmap); + if (accel) { + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; + } + break; + case 16: if (minfo->fbcon.var.green.length == 5) + maccess = 0xC0000001; + else + maccess = 0x40000001; + mopmode = M_OPMODE_16BPP; + if (accel) { + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; + } + break; + case 24: maccess = 0x00000003; + mopmode = M_OPMODE_24BPP; + if (accel) { + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; + } + break; + case 32: maccess = 0x00000002; + mopmode = M_OPMODE_32BPP; + if (accel) { + minfo->fbops.fb_copyarea = matroxfb_copyarea; + minfo->fbops.fb_fillrect = matroxfb_fillrect; + minfo->fbops.fb_imageblit = matroxfb_imageblit; + } + break; + default: maccess = 0x00000000; + mopmode = 0x00000000; + break; /* turn off acceleration!!! */ + } + mga_fifo(8); + mga_outl(M_PITCH, mpitch); + mga_outl(M_YDSTORG, curr_ydstorg(minfo)); + if (minfo->capable.plnwt) + mga_outl(M_PLNWT, -1); + if (minfo->capable.srcorg) { + mga_outl(M_SRCORG, 0); + mga_outl(M_DSTORG, 0); + } + mga_outl(M_OPMODE, mopmode); + mga_outl(M_CXBNDRY, 0xFFFF0000); + mga_outl(M_YTOP, 0); + mga_outl(M_YBOT, 0x01FFFFFF); + mga_outl(M_MACCESS, maccess); + minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; + if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC; + minfo->accel.m_opmode = mopmode; + minfo->accel.m_access = maccess; + minfo->accel.m_pitch = mpitch; +} + +EXPORT_SYMBOL(matrox_cfbX_init); + +static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo) +{ + mga_outl(M_MACCESS, minfo->accel.m_access); + mga_outl(M_PITCH, minfo->accel.m_pitch); +} + +static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy, + int sx, int dy, int dx, int height, int width) +{ + int start, end; + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + if ((dy < sy) || ((dy == sy) && (dx <= sx))) { + mga_fifo(4); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | + M_DWG_BFCOL | M_DWG_REPLACE); + mga_outl(M_AR5, vxres); + width--; + start = sy*vxres+sx+curr_ydstorg(minfo); + end = start+width; + } else { + mga_fifo(5); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); + mga_outl(M_SGN, 5); + mga_outl(M_AR5, -vxres); + width--; + end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); + start = end+width; + dy += height-1; + } + mga_fifo(6); + matrox_accel_restore_maccess(minfo); + mga_outl(M_AR0, end); + mga_outl(M_AR3, start); + mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); + mga_ydstlen(dy, height); + WaitTillIdle(); + + CRITEND +} + +static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres, + int sy, int sx, int dy, int dx, int height, + int width) +{ + int start, end; + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + if ((dy < sy) || ((dy == sy) && (dx <= sx))) { + mga_fifo(4); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | + M_DWG_BFCOL | M_DWG_REPLACE); + mga_outl(M_AR5, vxres); + width--; + start = sy*vxres+sx+curr_ydstorg(minfo); + end = start+width; + } else { + mga_fifo(5); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); + mga_outl(M_SGN, 5); + mga_outl(M_AR5, -vxres); + width--; + end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); + start = end+width; + dy += height-1; + } + mga_fifo(7); + matrox_accel_restore_maccess(minfo); + mga_outl(M_AR0, end); + mga_outl(M_AR3, start); + mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); + mga_outl(M_YDST, dy*vxres >> 5); + mga_outl(M_LEN | M_EXEC, height); + WaitTillIdle(); + + CRITEND +} + +static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) { + struct matrox_fb_info *minfo = info2minfo(info); + + if ((area->sx | area->dx | area->width) & 1) + cfb_copyarea(info, area); + else + matrox_accel_bmove_lin(minfo, minfo->fbcon.var.xres_virtual >> 1, area->sy, area->sx >> 1, area->dy, area->dx >> 1, area->height, area->width >> 1); +} + +static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) { + struct matrox_fb_info *minfo = info2minfo(info); + + matrox_accel_bmove(minfo, minfo->fbcon.var.xres_virtual, area->sy, area->sx, area->dy, area->dx, area->height, area->width); +} + +static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color, + int sy, int sx, int height, int width) +{ + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + mga_fifo(7); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE); + mga_outl(M_FCOL, color); + mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); + mga_ydstlen(sy, height); + WaitTillIdle(); + + CRITEND +} + +static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { + struct matrox_fb_info *minfo = info2minfo(info); + + switch (rect->rop) { + case ROP_COPY: + matroxfb_accel_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); + break; + } +} + +static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx, + int sy, int sx, int height, int width) +{ + int whattodo; + CRITFLAGS + + DBG(__func__) + + CRITBEGIN + + whattodo = 0; + if (sx & 1) { + sx ++; + if (!width) return; + width --; + whattodo = 1; + } + if (width & 1) { + whattodo |= 2; + } + width >>= 1; + sx >>= 1; + if (width) { + mga_fifo(7); + matrox_accel_restore_maccess(minfo); + mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2); + mga_outl(M_FCOL, bgx); + mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); + mga_outl(M_YDST, sy * minfo->fbcon.var.xres_virtual >> 6); + mga_outl(M_LEN | M_EXEC, height); + WaitTillIdle(); + } + if (whattodo) { + u_int32_t step = minfo->fbcon.var.xres_virtual >> 1; + vaddr_t vbase = minfo->video.vbase; + if (whattodo & 1) { + unsigned int uaddr = sy * step + sx - 1; + u_int32_t loop; + u_int8_t bgx2 = bgx & 0xF0; + for (loop = height; loop > 0; loop --) { + mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0x0F) | bgx2); + uaddr += step; + } + } + if (whattodo & 2) { + unsigned int uaddr = sy * step + sx + width; + u_int32_t loop; + u_int8_t bgx2 = bgx & 0x0F; + for (loop = height; loop > 0; loop --) { + mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0xF0) | bgx2); + uaddr += step; + } + } + } + + CRITEND +} + +static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { + struct matrox_fb_info *minfo = info2minfo(info); + + switch (rect->rop) { + case ROP_COPY: + matroxfb_cfb4_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); + break; + } +} + +static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx, + u_int32_t bgx, const u_int8_t *chardata, + int width, int height, int yy, int xx) +{ + u_int32_t step; + u_int32_t ydstlen; + u_int32_t xlen; + u_int32_t ar0; + u_int32_t charcell; + u_int32_t fxbndry; + vaddr_t mmio; + int easy; + CRITFLAGS + + DBG_HEAVY(__func__); + + step = (width + 7) >> 3; + charcell = height * step; + xlen = (charcell + 3) & ~3; + ydstlen = (yy << 16) | height; + if (width == step << 3) { + ar0 = height * width - 1; + easy = 1; + } else { + ar0 = width - 1; + easy = 0; + } + + CRITBEGIN + + mga_fifo(5); + matrox_accel_restore_maccess(minfo); + if (easy) + mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE); + else + mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE); + mga_outl(M_FCOL, fgx); + mga_outl(M_BCOL, bgx); + fxbndry = ((xx + width - 1) << 16) | xx; + mmio = minfo->mmio.vbase; + + mga_fifo(8); + matrox_accel_restore_maccess(minfo); + mga_writel(mmio, M_FXBNDRY, fxbndry); + mga_writel(mmio, M_AR0, ar0); + mga_writel(mmio, M_AR3, 0); + if (easy) { + mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); + mga_memcpy_toio(mmio, chardata, xlen); + } else { + mga_writel(mmio, M_AR5, 0); + mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); + if ((step & 3) == 0) { + /* Great. Source has 32bit aligned lines, so we can feed them + directly to the accelerator. */ + mga_memcpy_toio(mmio, chardata, charcell); + } else if (step == 1) { + /* Special case for 1..8bit widths */ + while (height--) { +#if defined(__BIG_ENDIAN) + fb_writel((*chardata) << 24, mmio.vaddr); +#else + fb_writel(*chardata, mmio.vaddr); +#endif + chardata++; + } + } else if (step == 2) { + /* Special case for 9..15bit widths */ + while (height--) { +#if defined(__BIG_ENDIAN) + fb_writel((*(u_int16_t*)chardata) << 16, mmio.vaddr); +#else + fb_writel(*(u_int16_t*)chardata, mmio.vaddr); +#endif + chardata += 2; + } + } else { + /* Tell... well, why bother... */ + while (height--) { + size_t i; + + for (i = 0; i < step; i += 4) { + /* Hope that there are at least three readable bytes beyond the end of bitmap */ + fb_writel(get_unaligned((u_int32_t*)(chardata + i)),mmio.vaddr); + } + chardata += step; + } + } + } + WaitTillIdle(); + CRITEND +} + + +static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) { + struct matrox_fb_info *minfo = info2minfo(info); + + DBG_HEAVY(__func__); + + if (image->depth == 1) { + u_int32_t fgx, bgx; + + fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; + bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color]; + matroxfb_1bpp_imageblit(minfo, fgx, bgx, image->data, image->width, image->height, image->dy, image->dx); + } else { + /* Danger! image->depth is useless: logo painting code always + passes framebuffer color depth here, although logo data are + always 8bpp and info->pseudo_palette is changed to contain + logo palette to be used (but only for true/direct-color... sic...). + So do it completely in software... */ + cfb_imageblit(info, image); + } +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_accel.h b/drivers/staging/mgakms/matroxfb_accel.h new file mode 100644 index 000000000000..a7aa9a1d26c0 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_accel.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_ACCEL_H__ +#define __MATROXFB_ACCEL_H__ + +#include "matroxfb_base.h" + +void matrox_cfbX_init(struct matrox_fb_info *minfo); + +#endif diff --git a/drivers/staging/mgakms/matroxfb_base.c b/drivers/staging/mgakms/matroxfb_base.c new file mode 100644 index 000000000000..d11b5e6210ed --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_base.c @@ -0,0 +1,2607 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.65 2002/08/14 + * + * MTRR stuff: 1998 Tom Rini trini@kernel.crashing.org + * + * Contributors: "menion?" menion@mindless.com + * Betatesting, fixes, ideas + * + * "Kurt Garloff" garloff@suse.de + * Betatesting, fixes, ideas, videomodes, videomodes timmings + * + * "Tom Rini" trini@kernel.crashing.org + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas + * + * "Bibek Sahu" scorpio@dodds.net + * Access device through readb|w|l and write b|w|l + * Extensive debugging stuff + * + * "Daniel Haun" haund@usa.net + * Testing, hardware cursor fixes + * + * "Scott Wood" sawst46+@pitt.edu + * Fixes + * + * "Gerd Knorr" kraxel@goldbach.isdn.cs.tu-berlin.de + * Betatesting + * + * "Kelly French" targon@hazmat.com + * "Fernando Herrera" fherrera@eurielec.etsit.upm.es + * Betatesting, bug reporting + * + * "Pablo Bianucci" pbian@pccp.com.ar + * Fixes, ideas, betatesting + * + * "Inaky Perez Gonzalez" inaky@peloncho.fis.ucm.es + * Fixes, enhandcements, ideas, betatesting + * + * "Ryuichi Oikawa" roikawa@rr.iiij4u.or.jp + * PPC betatesting, PPC support, backward compatibility + * + * "Paul Womar" Paul@pwomar.demon.co.uk + * "Owen Waller" O.Waller@ee.qub.ac.uk + * PPC betatesting + * + * "Thomas Pornin" pornin@bolet.ens.fr + * Alpha betatesting + * + * "Pieter van Leuven" pvl@iae.nl + * "Ulf Jaenicke-Roessler" ujr@physik.phy.tu-dresden.de + * G100 testing + * + * "H. Peter Arvin" hpa@transmeta.com + * Ideas + * + * "Cort Dougan" cort@cs.nmt.edu + * CHRP fixes and PReP cleanup + * + * "Mark Vojkovich" mvojkovi@ucsd.edu + * G400 support + * + * "Samuel Hocevar" sam@via.ecp.fr + * Fixes + * + * "Anton Altaparmakov" AntonA@bigfoot.com + * G400 MAX/non-MAX distinction + * + * "Ken Aaker" kdaaker@rchland.vnet.ibm.com + * memtype extension (needed for GXT130P RS/6000 adapter) + * + * "Uns Lider" unslider@miranda.org + * G100 PLNWT fixes + * + * "Denis Zaitsev" zzz@cd-club.ru + * Fixes + * + * "Mike Pieper" mike@pieper-family.de + * TVOut enhandcements, V4L2 control interface. + * + * "Diego Biurrun" diego@biurrun.de + * DFP testing + * + * (following author is not in any relation with this code, but his code + * is included in this driver) + * + * Based on framebuffer driver for VBE 2.0 compliant graphic boards + * (c) 1998 Gerd Knorr kraxel@cs.tu-berlin.de + * + * (following author is not in any relation with this code, but his ideas + * were used when writing this driver) + * + * FreeVBE/AF (Matrox), "Shawn Hargreaves" shawn@talula.demon.co.uk + * + */ + +#include <linux/version.h> + +#include "matroxfb_base.h" +#include "matroxfb_misc.h" +#include "matroxfb_accel.h" +#include "matroxfb_DAC1064.h" +#include "matroxfb_Ti3026.h" +#include "matroxfb_maven.h" +#include "matroxfb_crtc2.h" +#include "matroxfb_g450.h" +#include <linux/matroxfb.h> +#include <linux/interrupt.h> +#include <linux/nvram.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#ifdef CONFIG_PPC_PMAC +#include <asm/machdep.h> +static int default_vmode = VMODE_NVRAM; +static int default_cmode = CMODE_NVRAM; +#endif + +static void matroxfb_unregister_device(struct matrox_fb_info* minfo); + +/* --------------------------------------------------------------------- */ + +/* + * card parameters + */ + +/* --------------------------------------------------------------------- */ + +static struct fb_var_screeninfo vesafb_defined = { + 640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/ + 0,0, /* virtual -> visible no offset */ + 8, /* depth -> load bits_per_pixel */ + 0, /* greyscale ? */ + {0,0,0}, /* R */ + {0,0,0}, /* G */ + {0,0,0}, /* B */ + {0,0,0}, /* transparency */ + 0, /* standard pixel format */ + FB_ACTIVATE_NOW, + -1,-1, + FB_ACCELF_TEXT, /* accel flags */ + 39721L,48L,16L,33L,10L, + 96L,2L,~0, /* No sync info */ + FB_VMODE_NONINTERLACED, +}; + + + +/* --------------------------------------------------------------------- */ +static void update_crtc2(struct matrox_fb_info *minfo, unsigned int pos) +{ + struct matroxfb_dh_fb_info *info = minfo->crtc2.info; + + /* Make sure that displays are compatible */ + if (info && (info->fbcon.var.bits_per_pixel == minfo->fbcon.var.bits_per_pixel) + && (info->fbcon.var.xres_virtual == minfo->fbcon.var.xres_virtual) + && (info->fbcon.var.green.length == minfo->fbcon.var.green.length) + ) { + switch (minfo->fbcon.var.bits_per_pixel) { + case 16: + case 32: + pos = pos * 8; + if (info->interlaced) { + mga_outl(0x3C2C, pos); + mga_outl(0x3C28, pos + minfo->fbcon.var.xres_virtual * minfo->fbcon.var.bits_per_pixel / 8); + } else { + mga_outl(0x3C28, pos); + } + break; + } + } +} + +static void matroxfb_crtc1_panpos(struct matrox_fb_info *minfo) +{ + if (minfo->crtc1.panpos >= 0) { + unsigned long flags; + int panpos; + + matroxfb_DAC_lock_irqsave(flags); + panpos = minfo->crtc1.panpos; + if (panpos >= 0) { + unsigned int extvga_reg; + + minfo->crtc1.panpos = -1; /* No update pending anymore */ + extvga_reg = mga_inb(M_EXTVGA_INDEX); + mga_setr(M_EXTVGA_INDEX, 0x00, panpos); + if (extvga_reg != 0x00) { + mga_outb(M_EXTVGA_INDEX, extvga_reg); + } + } + matroxfb_DAC_unlock_irqrestore(flags); + } +} + +static irqreturn_t matrox_irq(int irq, void *dev_id) +{ + u_int32_t status; + int handled = 0; + struct matrox_fb_info *minfo = dev_id; + + status = mga_inl(M_STATUS); + + if (status & 0x20) { + mga_outl(M_ICLEAR, 0x20); + minfo->crtc1.vsync.cnt++; + matroxfb_crtc1_panpos(minfo); + wake_up_interruptible(&minfo->crtc1.vsync.wait); + handled = 1; + } + if (status & 0x200) { + mga_outl(M_ICLEAR, 0x200); + minfo->crtc2.vsync.cnt++; + wake_up_interruptible(&minfo->crtc2.vsync.wait); + handled = 1; + } + return IRQ_RETVAL(handled); +} + +int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable) +{ + u_int32_t bm; + + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) + bm = 0x220; + else + bm = 0x020; + + if (!test_and_set_bit(0, &minfo->irq_flags)) { + if (request_irq(minfo->pcidev->irq, matrox_irq, + IRQF_SHARED, "matroxfb", minfo)) { + clear_bit(0, &minfo->irq_flags); + return -EINVAL; + } + /* Clear any pending field interrupts */ + mga_outl(M_ICLEAR, bm); + mga_outl(M_IEN, mga_inl(M_IEN) | bm); + } else if (reenable) { + u_int32_t ien; + + ien = mga_inl(M_IEN); + if ((ien & bm) != bm) { + printk(KERN_DEBUG "matroxfb: someone disabled IRQ [%08X]\n", ien); + mga_outl(M_IEN, ien | bm); + } + } + return 0; +} + +static void matroxfb_disable_irq(struct matrox_fb_info *minfo) +{ + if (test_and_clear_bit(0, &minfo->irq_flags)) { + /* Flush pending pan-at-vbl request... */ + matroxfb_crtc1_panpos(minfo); + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) + mga_outl(M_IEN, mga_inl(M_IEN) & ~0x220); + else + mga_outl(M_IEN, mga_inl(M_IEN) & ~0x20); + free_irq(minfo->pcidev->irq, minfo); + } +} + +int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc) +{ + struct matrox_vsync *vs; + unsigned int cnt; + int ret; + + switch (crtc) { + case 0: + vs = &minfo->crtc1.vsync; + break; + case 1: + if (minfo->devflags.accelerator != FB_ACCEL_MATROX_MGAG400) { + return -ENODEV; + } + vs = &minfo->crtc2.vsync; + break; + default: + return -ENODEV; + } + ret = matroxfb_enable_irq(minfo, 0); + if (ret) { + return ret; + } + + cnt = vs->cnt; + ret = wait_event_interruptible_timeout(vs->wait, cnt != vs->cnt, HZ/10); + if (ret < 0) { + return ret; + } + if (ret == 0) { + matroxfb_enable_irq(minfo, 1); + return -ETIMEDOUT; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void matrox_pan_var(struct matrox_fb_info *minfo, + struct fb_var_screeninfo *var) +{ + unsigned int pos; + unsigned short p0, p1, p2; + unsigned int p3; + int vbl; + unsigned long flags; + + CRITFLAGS + + DBG(__func__) + + if (minfo->dead) + return; + + minfo->fbcon.var.xoffset = var->xoffset; + minfo->fbcon.var.yoffset = var->yoffset; + pos = (minfo->fbcon.var.yoffset * minfo->fbcon.var.xres_virtual + minfo->fbcon.var.xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->curr.ydstorg.chunks; + p0 = minfo->hw.CRTC[0x0D] = pos & 0xFF; + p1 = minfo->hw.CRTC[0x0C] = (pos & 0xFF00) >> 8; + p2 = minfo->hw.CRTCEXT[0] = (minfo->hw.CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + p3 = minfo->hw.CRTCEXT[8] = pos >> 21; + + /* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */ + vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(minfo, 0) == 0); + + CRITBEGIN + + matroxfb_DAC_lock_irqsave(flags); + mga_setr(M_CRTC_INDEX, 0x0D, p0); + mga_setr(M_CRTC_INDEX, 0x0C, p1); + if (minfo->devflags.support32MB) + mga_setr(M_EXTVGA_INDEX, 0x08, p3); + if (vbl) { + minfo->crtc1.panpos = p2; + } else { + /* Abort any pending change */ + minfo->crtc1.panpos = -1; + mga_setr(M_EXTVGA_INDEX, 0x00, p2); + } + matroxfb_DAC_unlock_irqrestore(flags); + + update_crtc2(minfo, pos); + + CRITEND +} + +static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy) +{ + /* Currently we are holding big kernel lock on all dead & usecount updates. + * Destroy everything after all users release it. Especially do not unregister + * framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check + * for device unplugged when in use. + * In future we should point mmio.vbase & video.vbase somewhere where we can + * write data without causing too much damage... + */ + + minfo->dead = 1; + if (minfo->usecount) { + /* destroy it later */ + return; + } + matroxfb_unregister_device(minfo); + unregister_framebuffer(&minfo->fbcon); + matroxfb_g450_shutdown(minfo); + arch_phys_wc_del(minfo->wc_cookie); + iounmap(minfo->mmio.vbase.vaddr); + iounmap(minfo->video.vbase.vaddr); + release_mem_region(minfo->video.base, minfo->video.len_maximum); + release_mem_region(minfo->mmio.base, 16384); + kfree(minfo); +} + + /* + * Open/Release the frame buffer device + */ + +static int matroxfb_open(struct fb_info *info, int user) +{ + struct matrox_fb_info *minfo = info2minfo(info); + + DBG_LOOP(__func__) + + if (minfo->dead) { + return -ENXIO; + } + minfo->usecount++; + if (user) { + minfo->userusecount++; + } + return(0); +} + +static int matroxfb_release(struct fb_info *info, int user) +{ + struct matrox_fb_info *minfo = info2minfo(info); + + DBG_LOOP(__func__) + + if (user) { + if (0 == --minfo->userusecount) { + matroxfb_disable_irq(minfo); + } + } + if (!(--minfo->usecount) && minfo->dead) { + matroxfb_remove(minfo, 0); + } + return(0); +} + +static int matroxfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info* info) { + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + matrox_pan_var(minfo, var); + return 0; +} + +static int matroxfb_get_final_bppShift(const struct matrox_fb_info *minfo, + int bpp) +{ + int bppshft2; + + DBG(__func__) + + bppshft2 = bpp; + if (!bppshft2) { + return 8; + } + if (isInterleave(minfo)) + bppshft2 >>= 1; + if (minfo->devflags.video64bits) + bppshft2 >>= 1; + return bppshft2; +} + +static int matroxfb_test_and_set_rounding(const struct matrox_fb_info *minfo, + int xres, int bpp) +{ + int over; + int rounding; + + DBG(__func__) + + switch (bpp) { + case 0: return xres; + case 4: rounding = 128; + break; + case 8: rounding = 64; /* doc says 64; 32 is OK for G400 */ + break; + case 16: rounding = 32; + break; + case 24: rounding = 64; /* doc says 64; 32 is OK for G400 */ + break; + default: rounding = 16; + /* on G400, 16 really does not work */ + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) + rounding = 32; + break; + } + if (isInterleave(minfo)) { + rounding *= 2; + } + over = xres % rounding; + if (over) + xres += rounding-over; + return xres; +} + +static int matroxfb_pitch_adjust(const struct matrox_fb_info *minfo, int xres, + int bpp) +{ + const int* width; + int xres_new; + + DBG(__func__) + + if (!bpp) return xres; + + width = minfo->capable.vxres; + + if (minfo->devflags.precise_width) { + while (*width) { + if ((*width >= xres) && (matroxfb_test_and_set_rounding(minfo, *width, bpp) == *width)) { + break; + } + width++; + } + xres_new = *width; + } else { + xres_new = matroxfb_test_and_set_rounding(minfo, xres, bpp); + } + return xres_new; +} + +static int matroxfb_get_cmap_len(struct fb_var_screeninfo *var) { + + DBG(__func__) + + switch (var->bits_per_pixel) { + case 4: + return 16; /* pseudocolor... 16 entries HW palette */ + case 8: + return 256; /* pseudocolor... 256 entries HW palette */ + case 16: + return 16; /* directcolor... 16 entries SW palette */ + /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ + case 24: + return 16; /* directcolor... 16 entries SW palette */ + /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ + case 32: + return 16; /* directcolor... 16 entries SW palette */ + /* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */ + } + return 16; /* return something reasonable... or panic()? */ +} + +static int matroxfb_decode_var(const struct matrox_fb_info *minfo, + struct fb_var_screeninfo *var, int *visual, + int *video_cmap_len, unsigned int* ydstorg) +{ + struct RGBT { + unsigned char bpp; + struct { + unsigned char offset, + length; + } red, + green, + blue, + transp; + signed char visual; + }; + static const struct RGBT table[]= { + { 8,{ 0,8},{0,8},{0,8},{ 0,0},MX_VISUAL_PSEUDOCOLOR}, + {15,{10,5},{5,5},{0,5},{15,1},MX_VISUAL_DIRECTCOLOR}, + {16,{11,5},{5,6},{0,5},{ 0,0},MX_VISUAL_DIRECTCOLOR}, + {24,{16,8},{8,8},{0,8},{ 0,0},MX_VISUAL_DIRECTCOLOR}, + {32,{16,8},{8,8},{0,8},{24,8},MX_VISUAL_DIRECTCOLOR} + }; + struct RGBT const *rgbt; + unsigned int bpp = var->bits_per_pixel; + unsigned int vramlen; + unsigned int memlen; + + DBG(__func__) + + switch (bpp) { + case 4: if (!minfo->capable.cfb4) return -EINVAL; + break; + case 8: break; + case 16: break; + case 24: break; + case 32: break; + default: return -EINVAL; + } + *ydstorg = 0; + vramlen = minfo->video.len_usable; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + + var->xres_virtual = matroxfb_pitch_adjust(minfo, var->xres_virtual, bpp); + memlen = var->xres_virtual * bpp * var->yres_virtual / 8; + if (memlen > vramlen) { + var->yres_virtual = vramlen * 8 / (var->xres_virtual * bpp); + memlen = var->xres_virtual * bpp * var->yres_virtual / 8; + } + /* There is hardware bug that no line can cross 4MB boundary */ + /* give up for CFB24, it is impossible to easy workaround it */ + /* for other try to do something */ + if (!minfo->capable.cross4MB && (memlen > 0x400000)) { + if (bpp == 24) { + /* sorry */ + } else { + unsigned int linelen; + unsigned int m1 = linelen = var->xres_virtual * bpp / 8; + unsigned int m2 = PAGE_SIZE; /* or 128 if you do not need PAGE ALIGNED address */ + unsigned int max_yres; + + while (m1) { + while (m2 >= m1) m2 -= m1; + swap(m1, m2); + } + m2 = linelen * PAGE_SIZE / m2; + *ydstorg = m2 = 0x400000 % m2; + max_yres = (vramlen - m2) / linelen; + if (var->yres_virtual > max_yres) + var->yres_virtual = max_yres; + } + } + /* YDSTLEN contains only signed 16bit value */ + if (var->yres_virtual > 32767) + var->yres_virtual = 32767; + /* we must round yres/xres down, we already rounded y/xres_virtual up + if it was possible. We should return -EINVAL, but I disagree */ + if (var->yres_virtual < var->yres) + var->yres = var->yres_virtual; + if (var->xres_virtual < var->xres) + var->xres = var->xres_virtual; + if (var->xoffset + var->xres > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yoffset + var->yres > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + if (bpp == 16 && var->green.length == 5) { + bpp--; /* an artificial value - 15 */ + } + + for (rgbt = table; rgbt->bpp < bpp; rgbt++); +#define SETCLR(clr)\ + var->clr.offset = rgbt->clr.offset;\ + var->clr.length = rgbt->clr.length + SETCLR(red); + SETCLR(green); + SETCLR(blue); + SETCLR(transp); +#undef SETCLR + *visual = rgbt->visual; + + if (bpp > 8) + dprintk("matroxfb: truecolor: " + "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", + var->transp.length, var->red.length, var->green.length, var->blue.length, + var->transp.offset, var->red.offset, var->green.offset, var->blue.offset); + + *video_cmap_len = matroxfb_get_cmap_len(var); + dprintk(KERN_INFO "requested %d*%d/%dbpp (%d*%d)\n", var->xres, var->yres, var->bits_per_pixel, + var->xres_virtual, var->yres_virtual); + return 0; +} + +static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *fb_info) +{ + struct matrox_fb_info* minfo = container_of(fb_info, struct matrox_fb_info, fbcon); + + DBG(__func__) + + /* + * Set a single color register. The values supplied are + * already rounded down to the hardware's capabilities + * (according to the entries in the `var' structure). Return + * != 0 for invalid regno. + */ + + if (regno >= minfo->curr.cmap_len) + return 1; + + if (minfo->fbcon.var.grayscale) { + /* gray = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + red = CNVT_TOHW(red, minfo->fbcon.var.red.length); + green = CNVT_TOHW(green, minfo->fbcon.var.green.length); + blue = CNVT_TOHW(blue, minfo->fbcon.var.blue.length); + transp = CNVT_TOHW(transp, minfo->fbcon.var.transp.length); + + switch (minfo->fbcon.var.bits_per_pixel) { + case 4: + case 8: + mga_outb(M_DAC_REG, regno); + mga_outb(M_DAC_VAL, red); + mga_outb(M_DAC_VAL, green); + mga_outb(M_DAC_VAL, blue); + break; + case 16: + if (regno >= 16) + break; + { + u_int16_t col = + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* for 1:5:5:5 */ + minfo->cmap[regno] = col | (col << 16); + } + break; + case 24: + case 32: + if (regno >= 16) + break; + minfo->cmap[regno] = + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* 8:8:8:8 */ + break; + } + return 0; +} + +static void matroxfb_init_fix(struct matrox_fb_info *minfo) +{ + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; + DBG(__func__) + + strcpy(fix->id,"MATROX"); + + fix->xpanstep = 8; /* 8 for 8bpp, 4 for 16bpp, 2 for 32bpp */ + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->mmio_start = minfo->mmio.base; + fix->mmio_len = minfo->mmio.len; + fix->accel = minfo->devflags.accelerator; +} + +static void matroxfb_update_fix(struct matrox_fb_info *minfo) +{ + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; + DBG(__func__) + + mutex_lock(&minfo->fbcon.mm_lock); + fix->smem_start = minfo->video.base + minfo->curr.ydstorg.bytes; + fix->smem_len = minfo->video.len_usable - minfo->curr.ydstorg.bytes; + mutex_unlock(&minfo->fbcon.mm_lock); +} + +static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + int err; + int visual; + int cmap_len; + unsigned int ydstorg; + struct matrox_fb_info *minfo = info2minfo(info); + + if (minfo->dead) { + return -ENXIO; + } + if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) + return err; + return 0; +} + +static int matroxfb_set_par(struct fb_info *info) +{ + int err; + int visual; + int cmap_len; + unsigned int ydstorg; + struct fb_var_screeninfo *var; + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + if (minfo->dead) { + return -ENXIO; + } + + var = &info->var; + if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) + return err; + minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase) + ydstorg; + matroxfb_update_fix(minfo); + minfo->fbcon.fix.visual = visual; + minfo->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; + minfo->fbcon.fix.type_aux = 0; + minfo->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; + { + unsigned int pos; + + minfo->curr.cmap_len = cmap_len; + ydstorg += minfo->devflags.ydstorg; + minfo->curr.ydstorg.bytes = ydstorg; + minfo->curr.ydstorg.chunks = ydstorg >> (isInterleave(minfo) ? 3 : 2); + if (var->bits_per_pixel == 4) + minfo->curr.ydstorg.pixels = ydstorg; + else + minfo->curr.ydstorg.pixels = (ydstorg * 8) / var->bits_per_pixel; + minfo->curr.final_bppShift = matroxfb_get_final_bppShift(minfo, var->bits_per_pixel); + { struct my_timming mt; + struct matrox_hw_state* hw; + int out; + + matroxfb_var2my(var, &mt); + mt.crtc = MATROXFB_SRC_CRTC1; + /* CRTC1 delays */ + switch (var->bits_per_pixel) { + case 0: mt.delay = 31 + 0; break; + case 16: mt.delay = 21 + 8; break; + case 24: mt.delay = 17 + 8; break; + case 32: mt.delay = 16 + 8; break; + default: mt.delay = 31 + 8; break; + } + + hw = &minfo->hw; + + down_read(&minfo->altout.lock); + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->compute) { + minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); + } + } + up_read(&minfo->altout.lock); + minfo->crtc1.pixclock = mt.pixclock; + minfo->crtc1.mnp = mt.mnp; + minfo->hw_switch->init(minfo, &mt); + pos = (var->yoffset * var->xres_virtual + var->xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->curr.ydstorg.chunks; + + hw->CRTC[0x0D] = pos & 0xFF; + hw->CRTC[0x0C] = (pos & 0xFF00) >> 8; + hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + hw->CRTCEXT[8] = pos >> 21; + minfo->hw_switch->restore(minfo); + update_crtc2(minfo, pos); + down_read(&minfo->altout.lock); + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->program) { + minfo->outputs[out].output->program(minfo->outputs[out].data); + } + } + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->start) { + minfo->outputs[out].output->start(minfo->outputs[out].data); + } + } + up_read(&minfo->altout.lock); + matrox_cfbX_init(minfo); + } + } + minfo->initialized = 1; + return 0; +} + +static int matroxfb_get_vblank(struct matrox_fb_info *minfo, + struct fb_vblank *vblank) +{ + unsigned int sts1; + + matroxfb_enable_irq(minfo, 0); + memset(vblank, 0, sizeof(*vblank)); + vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC | + FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_HBLANK; + sts1 = mga_inb(M_INSTS1); + vblank->vcount = mga_inl(M_VCOUNT); + /* BTW, on my PIII/450 with G400, reading M_INSTS1 + byte makes this call about 12% slower (1.70 vs. 2.05 us + per ioctl()) */ + if (sts1 & 1) + vblank->flags |= FB_VBLANK_HBLANKING; + if (sts1 & 8) + vblank->flags |= FB_VBLANK_VSYNCING; + if (vblank->vcount >= minfo->fbcon.var.yres) + vblank->flags |= FB_VBLANK_VBLANKING; + if (test_bit(0, &minfo->irq_flags)) { + vblank->flags |= FB_VBLANK_HAVE_COUNT; + /* Only one writer, aligned int value... + it should work without lock and without atomic_t */ + vblank->count = minfo->crtc1.vsync.cnt; + } + return 0; +} + +static struct matrox_altout panellink_output = { + .name = "Panellink output", +}; + +static int matroxfb_ioctl(struct fb_info *info, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + if (minfo->dead) { + return -ENXIO; + } + + switch (cmd) { + case FBIOGET_VBLANK: + { + struct fb_vblank vblank; + int err; + + err = matroxfb_get_vblank(minfo, &vblank); + if (err) + return err; + if (copy_to_user(argp, &vblank, sizeof(vblank))) + return -EFAULT; + return 0; + } + case FBIO_WAITFORVSYNC: + { + u_int32_t crt; + + if (get_user(crt, (u_int32_t __user *)arg)) + return -EFAULT; + + return matroxfb_wait_for_sync(minfo, crt); + } + case MATROXFB_SET_OUTPUT_MODE: + { + struct matroxioc_output_mode mom; + struct matrox_altout *oproc; + int val; + + if (copy_from_user(&mom, argp, sizeof(mom))) + return -EFAULT; + if (mom.output >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + down_read(&minfo->altout.lock); + oproc = minfo->outputs[mom.output].output; + if (!oproc) { + val = -ENXIO; + } else if (!oproc->verifymode) { + if (mom.mode == MATROXFB_OUTPUT_MODE_MONITOR) { + val = 0; + } else { + val = -EINVAL; + } + } else { + val = oproc->verifymode(minfo->outputs[mom.output].data, mom.mode); + } + if (!val) { + if (minfo->outputs[mom.output].mode != mom.mode) { + minfo->outputs[mom.output].mode = mom.mode; + val = 1; + } + } + up_read(&minfo->altout.lock); + if (val != 1) + return val; + switch (minfo->outputs[mom.output].src) { + case MATROXFB_SRC_CRTC1: + matroxfb_set_par(info); + break; + case MATROXFB_SRC_CRTC2: + { + struct matroxfb_dh_fb_info* crtc2; + + down_read(&minfo->crtc2.lock); + crtc2 = minfo->crtc2.info; + if (crtc2) + crtc2->fbcon.fbops->fb_set_par(&crtc2->fbcon); + up_read(&minfo->crtc2.lock); + } + break; + } + return 0; + } + case MATROXFB_GET_OUTPUT_MODE: + { + struct matroxioc_output_mode mom; + struct matrox_altout *oproc; + int val; + + if (copy_from_user(&mom, argp, sizeof(mom))) + return -EFAULT; + if (mom.output >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + down_read(&minfo->altout.lock); + oproc = minfo->outputs[mom.output].output; + if (!oproc) { + val = -ENXIO; + } else { + mom.mode = minfo->outputs[mom.output].mode; + val = 0; + } + up_read(&minfo->altout.lock); + if (val) + return val; + if (copy_to_user(argp, &mom, sizeof(mom))) + return -EFAULT; + return 0; + } + case MATROXFB_SET_OUTPUT_CONNECTION: + { + u_int32_t tmp; + int i; + int changes; + + if (copy_from_user(&tmp, argp, sizeof(tmp))) + return -EFAULT; + for (i = 0; i < 32; i++) { + if (tmp & (1 << i)) { + if (i >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + if (!minfo->outputs[i].output) + return -ENXIO; + switch (minfo->outputs[i].src) { + case MATROXFB_SRC_NONE: + case MATROXFB_SRC_CRTC1: + break; + default: + return -EBUSY; + } + } + } + if (minfo->devflags.panellink) { + if (tmp & MATROXFB_OUTPUT_CONN_DFP) { + if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY) + return -EINVAL; + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (minfo->outputs[i].src == MATROXFB_SRC_CRTC2) { + return -EBUSY; + } + } + } + } + changes = 0; + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (tmp & (1 << i)) { + if (minfo->outputs[i].src != MATROXFB_SRC_CRTC1) { + changes = 1; + minfo->outputs[i].src = MATROXFB_SRC_CRTC1; + } + } else if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { + changes = 1; + minfo->outputs[i].src = MATROXFB_SRC_NONE; + } + } + if (!changes) + return 0; + matroxfb_set_par(info); + return 0; + } + case MATROXFB_GET_OUTPUT_CONNECTION: + { + u_int32_t conn = 0; + int i; + + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { + conn |= 1 << i; + } + } + if (put_user(conn, (u_int32_t __user *)arg)) + return -EFAULT; + return 0; + } + case MATROXFB_GET_AVAILABLE_OUTPUTS: + { + u_int32_t conn = 0; + int i; + + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (minfo->outputs[i].output) { + switch (minfo->outputs[i].src) { + case MATROXFB_SRC_NONE: + case MATROXFB_SRC_CRTC1: + conn |= 1 << i; + break; + } + } + } + if (minfo->devflags.panellink) { + if (conn & MATROXFB_OUTPUT_CONN_DFP) + conn &= ~MATROXFB_OUTPUT_CONN_SECONDARY; + if (conn & MATROXFB_OUTPUT_CONN_SECONDARY) + conn &= ~MATROXFB_OUTPUT_CONN_DFP; + } + if (put_user(conn, (u_int32_t __user *)arg)) + return -EFAULT; + return 0; + } + case MATROXFB_GET_ALL_OUTPUTS: + { + u_int32_t conn = 0; + int i; + + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (minfo->outputs[i].output) { + conn |= 1 << i; + } + } + if (put_user(conn, (u_int32_t __user *)arg)) + return -EFAULT; + return 0; + } + case VIDIOC_QUERYCAP: + { + struct v4l2_capability r; + + memset(&r, 0, sizeof(r)); + strcpy(r.driver, "matroxfb"); + strcpy(r.card, "Matrox"); + sprintf(r.bus_info, "PCI:%s", pci_name(minfo->pcidev)); + r.version = KERNEL_VERSION(1,0,0); + r.capabilities = V4L2_CAP_VIDEO_OUTPUT; + if (copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; + return 0; + + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl qctrl; + int err; + + if (copy_from_user(&qctrl, argp, sizeof(qctrl))) + return -EFAULT; + + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { + err = -ENXIO; + } else if (minfo->outputs[1].output->getqueryctrl) { + err = minfo->outputs[1].output->getqueryctrl(minfo->outputs[1].data, &qctrl); + } else { + err = -EINVAL; + } + up_read(&minfo->altout.lock); + if (err >= 0 && + copy_to_user(argp, &qctrl, sizeof(qctrl))) + return -EFAULT; + return err; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control ctrl; + int err; + + if (copy_from_user(&ctrl, argp, sizeof(ctrl))) + return -EFAULT; + + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { + err = -ENXIO; + } else if (minfo->outputs[1].output->getctrl) { + err = minfo->outputs[1].output->getctrl(minfo->outputs[1].data, &ctrl); + } else { + err = -EINVAL; + } + up_read(&minfo->altout.lock); + if (err >= 0 && + copy_to_user(argp, &ctrl, sizeof(ctrl))) + return -EFAULT; + return err; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control ctrl; + int err; + + if (copy_from_user(&ctrl, argp, sizeof(ctrl))) + return -EFAULT; + + down_read(&minfo->altout.lock); + if (!minfo->outputs[1].output) { + err = -ENXIO; + } else if (minfo->outputs[1].output->setctrl) { + err = minfo->outputs[1].output->setctrl(minfo->outputs[1].data, &ctrl); + } else { + err = -EINVAL; + } + up_read(&minfo->altout.lock); + return err; + } + } + return -ENOTTY; +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ + +static int matroxfb_blank(int blank, struct fb_info *info) +{ + int seq; + int crtc; + CRITFLAGS + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + if (minfo->dead) + return 1; + + switch (blank) { + case FB_BLANK_NORMAL: seq = 0x20; crtc = 0x00; break; /* works ??? */ + case FB_BLANK_VSYNC_SUSPEND: seq = 0x20; crtc = 0x10; break; + case FB_BLANK_HSYNC_SUSPEND: seq = 0x20; crtc = 0x20; break; + case FB_BLANK_POWERDOWN: seq = 0x20; crtc = 0x30; break; + default: seq = 0x00; crtc = 0x00; break; + } + + CRITBEGIN + + mga_outb(M_SEQ_INDEX, 1); + mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq); + mga_outb(M_EXTVGA_INDEX, 1); + mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc); + + CRITEND + return 0; +} + +static const struct fb_ops matroxfb_ops = { + .owner = THIS_MODULE, + .fb_open = matroxfb_open, + .fb_release = matroxfb_release, + .fb_check_var = matroxfb_check_var, + .fb_set_par = matroxfb_set_par, + .fb_setcolreg = matroxfb_setcolreg, + .fb_pan_display =matroxfb_pan_display, + .fb_blank = matroxfb_blank, + .fb_ioctl = matroxfb_ioctl, +/* .fb_fillrect = <set by matrox_cfbX_init>, */ +/* .fb_copyarea = <set by matrox_cfbX_init>, */ +/* .fb_imageblit = <set by matrox_cfbX_init>, */ +/* .fb_cursor = <set by matrox_cfbX_init>, */ +}; + +#define RSDepth(X) (((X) >> 8) & 0x0F) +#define RS8bpp 0x1 +#define RS15bpp 0x2 +#define RS16bpp 0x3 +#define RS32bpp 0x4 +#define RS4bpp 0x5 +#define RS24bpp 0x6 +#define RSText 0x7 +#define RSText8 0x8 +/* 9-F */ +static struct { struct fb_bitfield red, green, blue, transp; int bits_per_pixel; } colors[] = { + { { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 8 }, + { { 10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 0}, 16 }, + { { 11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0}, 16 }, + { { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 24, 8, 0}, 32 }, + { { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 4 }, + { { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 24 }, + { { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode with (default) VGA8x16 */ + { { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode hardwired to VGA8x8 */ +}; + +/* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */ +static unsigned int mem; /* "matroxfb:mem:xxxxxM" */ +static int option_precise_width = 1; /* cannot be changed, option_precise_width==0 must imply noaccel */ +static int inv24; /* "matroxfb:inv24" */ +static int cross4MB = -1; /* "matroxfb:cross4MB" */ +static int disabled; /* "matroxfb:disabled" */ +static int noaccel; /* "matroxfb:noaccel" */ +static int nopan; /* "matroxfb:nopan" */ +static int no_pci_retry; /* "matroxfb:nopciretry" */ +static int novga; /* "matroxfb:novga" */ +static int nobios; /* "matroxfb:nobios" */ +static int noinit = 1; /* "matroxfb:init" */ +static int inverse; /* "matroxfb:inverse" */ +static int sgram; /* "matroxfb:sgram" */ +static int mtrr = 1; /* "matroxfb:nomtrr" */ +static int grayscale; /* "matroxfb:grayscale" */ +static int dev = -1; /* "matroxfb:dev:xxxxx" */ +static unsigned int vesa = ~0; /* "matroxfb:vesa:xxxxx" */ +static int depth = -1; /* "matroxfb:depth:xxxxx" */ +static unsigned int xres; /* "matroxfb:xres:xxxxx" */ +static unsigned int yres; /* "matroxfb:yres:xxxxx" */ +static unsigned int upper = ~0; /* "matroxfb:upper:xxxxx" */ +static unsigned int lower = ~0; /* "matroxfb:lower:xxxxx" */ +static unsigned int vslen; /* "matroxfb:vslen:xxxxx" */ +static unsigned int left = ~0; /* "matroxfb:left:xxxxx" */ +static unsigned int right = ~0; /* "matroxfb:right:xxxxx" */ +static unsigned int hslen; /* "matroxfb:hslen:xxxxx" */ +static unsigned int pixclock; /* "matroxfb:pixclock:xxxxx" */ +static int sync = -1; /* "matroxfb:sync:xxxxx" */ +static unsigned int fv; /* "matroxfb:fv:xxxxx" */ +static unsigned int fh; /* "matroxfb:fh:xxxxxk" */ +static unsigned int maxclk; /* "matroxfb:maxclk:xxxxM" */ +static int dfp; /* "matroxfb:dfp */ +static int dfp_type = -1; /* "matroxfb:dfp:xxx */ +static int memtype = -1; /* "matroxfb:memtype:xxx" */ +static char outputs[8]; /* "matroxfb:outputs:xxx" */ + +#ifndef MODULE +static char videomode[64]; /* "matroxfb:mode:xxxxx" or "matroxfb:xxxxx" */ +#endif + +static int matroxfb_getmemory(struct matrox_fb_info *minfo, + unsigned int maxSize, unsigned int *realSize) +{ + vaddr_t vm; + unsigned int offs; + unsigned int offs2; + unsigned char orig; + unsigned char bytes[32]; + unsigned char* tmp; + + DBG(__func__) + + vm = minfo->video.vbase; + maxSize &= ~0x1FFFFF; /* must be X*2MB (really it must be 2 or X*4MB) */ + /* at least 2MB */ + if (maxSize < 0x0200000) return 0; + if (maxSize > 0x2000000) maxSize = 0x2000000; + + mga_outb(M_EXTVGA_INDEX, 0x03); + orig = mga_inb(M_EXTVGA_DATA); + mga_outb(M_EXTVGA_DATA, orig | 0x80); + + tmp = bytes; + for (offs = 0x100000; offs < maxSize; offs += 0x200000) + *tmp++ = mga_readb(vm, offs); + for (offs = 0x100000; offs < maxSize; offs += 0x200000) + mga_writeb(vm, offs, 0x02); + mga_outb(M_CACHEFLUSH, 0x00); + for (offs = 0x100000; offs < maxSize; offs += 0x200000) { + if (mga_readb(vm, offs) != 0x02) + break; + mga_writeb(vm, offs, mga_readb(vm, offs) - 0x02); + if (mga_readb(vm, offs)) + break; + } + tmp = bytes; + for (offs2 = 0x100000; offs2 < maxSize; offs2 += 0x200000) + mga_writeb(vm, offs2, *tmp++); + + mga_outb(M_EXTVGA_INDEX, 0x03); + mga_outb(M_EXTVGA_DATA, orig); + + *realSize = offs - 0x100000; +#ifdef CONFIG_FB_MATROX_MILLENIUM + minfo->interleave = !(!isMillenium(minfo) || ((offs - 0x100000) & 0x3FFFFF)); +#endif + return 1; +} + +struct video_board { + int maxvram; + int maxdisplayable; + int accelID; + struct matrox_switch* lowlevel; + }; +#ifdef CONFIG_FB_MATROX_MILLENIUM +static struct video_board vbMillennium = { + .maxvram = 0x0800000, + .maxdisplayable = 0x0800000, + .accelID = FB_ACCEL_MATROX_MGA2064W, + .lowlevel = &matrox_millennium +}; + +static struct video_board vbMillennium2 = { + .maxvram = 0x1000000, + .maxdisplayable = 0x0800000, + .accelID = FB_ACCEL_MATROX_MGA2164W, + .lowlevel = &matrox_millennium +}; + +static struct video_board vbMillennium2A = { + .maxvram = 0x1000000, + .maxdisplayable = 0x0800000, + .accelID = FB_ACCEL_MATROX_MGA2164W_AGP, + .lowlevel = &matrox_millennium +}; +#endif /* CONFIG_FB_MATROX_MILLENIUM */ +#ifdef CONFIG_FB_MATROX_MYSTIQUE +static struct video_board vbMystique = { + .maxvram = 0x0800000, + .maxdisplayable = 0x0800000, + .accelID = FB_ACCEL_MATROX_MGA1064SG, + .lowlevel = &matrox_mystique +}; +#endif /* CONFIG_FB_MATROX_MYSTIQUE */ +#ifdef CONFIG_FB_MATROX_G +static struct video_board vbG100 = { + .maxvram = 0x0800000, + .maxdisplayable = 0x0800000, + .accelID = FB_ACCEL_MATROX_MGAG100, + .lowlevel = &matrox_G100 +}; + +static struct video_board vbG200 = { + .maxvram = 0x1000000, + .maxdisplayable = 0x1000000, + .accelID = FB_ACCEL_MATROX_MGAG200, + .lowlevel = &matrox_G100 +}; +/* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for + whole 32MB */ +static struct video_board vbG400 = { + .maxvram = 0x2000000, + .maxdisplayable = 0x1000000, + .accelID = FB_ACCEL_MATROX_MGAG400, + .lowlevel = &matrox_G100 +}; +#endif + +#define DEVF_VIDEO64BIT 0x0001 +#define DEVF_SWAPS 0x0002 +#define DEVF_SRCORG 0x0004 +#define DEVF_DUALHEAD 0x0008 +#define DEVF_CROSS4MB 0x0010 +#define DEVF_TEXT4B 0x0020 +/* #define DEVF_recycled 0x0040 */ +/* #define DEVF_recycled 0x0080 */ +#define DEVF_SUPPORT32MB 0x0100 +#define DEVF_ANY_VXRES 0x0200 +#define DEVF_TEXT16B 0x0400 +#define DEVF_CRTC2 0x0800 +#define DEVF_MAVEN_CAPABLE 0x1000 +#define DEVF_PANELLINK_CAPABLE 0x2000 +#define DEVF_G450DAC 0x4000 + +#define DEVF_GCORE (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB) +#define DEVF_G2CORE (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE | DEVF_SRCORG | DEVF_DUALHEAD) +#define DEVF_G100 (DEVF_GCORE) /* no doc, no vxres... */ +#define DEVF_G200 (DEVF_G2CORE) +#define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2) +/* if you'll find how to drive DFP... */ +#define DEVF_G450 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG | DEVF_DUALHEAD) +#define DEVF_G550 (DEVF_G450) + +static struct board { + unsigned short vendor, device, rev, svid, sid; + unsigned int flags; + unsigned int maxclk; + enum mga_chip chip; + struct video_board* base; + const char* name; + } dev_list[] = { +#ifdef CONFIG_FB_MATROX_MILLENIUM + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, 0xFF, + 0, 0, + DEVF_TEXT4B, + 230000, + MGA_2064, + &vbMillennium, + "Millennium (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, 0xFF, + 0, 0, + DEVF_SWAPS, + 220000, + MGA_2164, + &vbMillennium2, + "Millennium II (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, 0xFF, + 0, 0, + DEVF_SWAPS, + 250000, + MGA_2164, + &vbMillennium2A, + "Millennium II (AGP)"}, +#endif +#ifdef CONFIG_FB_MATROX_MYSTIQUE + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0x02, + 0, 0, + DEVF_VIDEO64BIT | DEVF_CROSS4MB, + 180000, + MGA_1064, + &vbMystique, + "Mystique (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0xFF, + 0, 0, + DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB, + 220000, + MGA_1164, + &vbMystique, + "Mystique 220 (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS_AGP, 0x02, + 0, 0, + DEVF_VIDEO64BIT | DEVF_CROSS4MB, + 180000, + MGA_1064, + &vbMystique, + "Mystique (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS_AGP, 0xFF, + 0, 0, + DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB, + 220000, + MGA_1164, + &vbMystique, + "Mystique 220 (AGP)"}, +#endif +#ifdef CONFIG_FB_MATROX_G + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, + 0, 0, + DEVF_G100, + 230000, + MGA_G100, + &vbG100, + "MGA-G100 (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF, + 0, 0, + DEVF_G100, + 230000, + MGA_G100, + &vbG100, + "MGA-G100 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, 0xFF, + 0, 0, + DEVF_G200, + 250000, + MGA_G200, + &vbG200, + "MGA-G200 (PCI)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC, + DEVF_G200, + 220000, + MGA_G200, + &vbG200, + "MGA-G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP, + DEVF_G200, + 230000, + MGA_G200, + &vbG200, + "Mystique G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENIUM_G200_AGP, + DEVF_G200, + 250000, + MGA_G200, + &vbG200, + "Millennium G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MARVEL_G200_AGP, + DEVF_G200, + 230000, + MGA_G200, + &vbG200, + "Marvel G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G200_AGP, + DEVF_G200, + 230000, + MGA_G200, + &vbG200, + "MGA-G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF, + 0, 0, + DEVF_G200, + 230000, + MGA_G200, + &vbG200, + "G200 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, + PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP, + DEVF_G400, + 360000, + MGA_G400, + &vbG400, + "Millennium G400 MAX (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80, + 0, 0, + DEVF_G400, + 300000, + MGA_G400, + &vbG400, + "G400 (AGP)"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0xFF, + 0, 0, + DEVF_G450, + 360000, + MGA_G450, + &vbG400, + "G450"}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, 0xFF, + 0, 0, + DEVF_G550, + 360000, + MGA_G550, + &vbG400, + "G550"}, +#endif + {0, 0, 0xFF, + 0, 0, + 0, + 0, + 0, + NULL, + NULL}}; + +#ifndef MODULE +static const struct fb_videomode defaultmode = { + /* 640x480 @ 60Hz, 31.5 kHz */ + NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, + 0, FB_VMODE_NONINTERLACED +}; + +static int hotplug = 0; +#endif /* !MODULE */ + +static void setDefaultOutputs(struct matrox_fb_info *minfo) +{ + unsigned int i; + const char* ptr; + + minfo->outputs[0].default_src = MATROXFB_SRC_CRTC1; + if (minfo->devflags.g450dac) { + minfo->outputs[1].default_src = MATROXFB_SRC_CRTC1; + minfo->outputs[2].default_src = MATROXFB_SRC_CRTC1; + } else if (dfp) { + minfo->outputs[2].default_src = MATROXFB_SRC_CRTC1; + } + ptr = outputs; + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + char c = *ptr++; + + if (c == 0) { + break; + } + if (c == '0') { + minfo->outputs[i].default_src = MATROXFB_SRC_NONE; + } else if (c == '1') { + minfo->outputs[i].default_src = MATROXFB_SRC_CRTC1; + } else if (c == '2' && minfo->devflags.crtc2) { + minfo->outputs[i].default_src = MATROXFB_SRC_CRTC2; + } else { + printk(KERN_ERR "matroxfb: Unknown outputs setting\n"); + break; + } + } + /* Nullify this option for subsequent adapters */ + outputs[0] = 0; +} + +static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) +{ + unsigned long ctrlptr_phys = 0; + unsigned long video_base_phys = 0; + unsigned int memsize; + int err; + + static const struct pci_device_id intel_82437[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437) }, + { }, + }; + + DBG(__func__) + + /* set default values... */ + vesafb_defined.accel_flags = FB_ACCELF_TEXT; + + minfo->hw_switch = b->base->lowlevel; + minfo->devflags.accelerator = b->base->accelID; + minfo->max_pixel_clock = b->maxclk; + + printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name); + minfo->capable.plnwt = 1; + minfo->chip = b->chip; + minfo->capable.srcorg = b->flags & DEVF_SRCORG; + minfo->devflags.video64bits = b->flags & DEVF_VIDEO64BIT; + if (b->flags & DEVF_TEXT4B) { + minfo->devflags.vgastep = 4; + minfo->devflags.textmode = 4; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP16; + } else if (b->flags & DEVF_TEXT16B) { + minfo->devflags.vgastep = 16; + minfo->devflags.textmode = 1; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP16; + } else { + minfo->devflags.vgastep = 8; + minfo->devflags.textmode = 1; + minfo->devflags.text_type_aux = FB_AUX_TEXT_MGA_STEP8; + } + minfo->devflags.support32MB = (b->flags & DEVF_SUPPORT32MB) != 0; + minfo->devflags.precise_width = !(b->flags & DEVF_ANY_VXRES); + minfo->devflags.crtc2 = (b->flags & DEVF_CRTC2) != 0; + minfo->devflags.maven_capable = (b->flags & DEVF_MAVEN_CAPABLE) != 0; + minfo->devflags.dualhead = (b->flags & DEVF_DUALHEAD) != 0; + minfo->devflags.dfp_type = dfp_type; + minfo->devflags.g450dac = (b->flags & DEVF_G450DAC) != 0; + minfo->devflags.textstep = minfo->devflags.vgastep * minfo->devflags.textmode; + minfo->devflags.textvram = 65536 / minfo->devflags.textmode; + setDefaultOutputs(minfo); + if (b->flags & DEVF_PANELLINK_CAPABLE) { + minfo->outputs[2].data = minfo; + minfo->outputs[2].output = &panellink_output; + minfo->outputs[2].src = minfo->outputs[2].default_src; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->devflags.panellink = 1; + } + + if (minfo->capable.cross4MB < 0) + minfo->capable.cross4MB = b->flags & DEVF_CROSS4MB; + if (b->flags & DEVF_SWAPS) { + ctrlptr_phys = pci_resource_start(minfo->pcidev, 1); + video_base_phys = pci_resource_start(minfo->pcidev, 0); + minfo->devflags.fbResource = PCI_BASE_ADDRESS_0; + } else { + ctrlptr_phys = pci_resource_start(minfo->pcidev, 0); + video_base_phys = pci_resource_start(minfo->pcidev, 1); + minfo->devflags.fbResource = PCI_BASE_ADDRESS_1; + } + err = -EINVAL; + if (!ctrlptr_phys) { + printk(KERN_ERR "matroxfb: control registers are not available, matroxfb disabled\n"); + goto fail; + } + if (!video_base_phys) { + printk(KERN_ERR "matroxfb: video RAM is not available in PCI address space, matroxfb disabled\n"); + goto fail; + } + memsize = b->base->maxvram; + if (!request_mem_region(ctrlptr_phys, 16384, "matroxfb MMIO")) { + goto fail; + } + if (!request_mem_region(video_base_phys, memsize, "matroxfb FB")) { + goto failCtrlMR; + } + minfo->video.len_maximum = memsize; + /* convert mem (autodetect k, M) */ + if (mem < 1024) mem *= 1024; + if (mem < 0x00100000) mem *= 1024; + + if (mem && (mem < memsize)) + memsize = mem; + err = -ENOMEM; + + minfo->mmio.vbase.vaddr = ioremap_nocache(ctrlptr_phys, 16384); + if (!minfo->mmio.vbase.vaddr) { + printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys); + goto failVideoMR; + } + minfo->mmio.base = ctrlptr_phys; + minfo->mmio.len = 16384; + minfo->video.base = video_base_phys; + minfo->video.vbase.vaddr = ioremap_wc(video_base_phys, memsize); + if (!minfo->video.vbase.vaddr) { + printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n", + video_base_phys, memsize); + goto failCtrlIO; + } + { + u_int32_t cmd; + u_int32_t mga_option; + + pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &mga_option); + pci_read_config_dword(minfo->pcidev, PCI_COMMAND, &cmd); + mga_option &= 0x7FFFFFFF; /* clear BIG_ENDIAN */ + mga_option |= MX_OPTION_BSWAP; + /* disable palette snooping */ + cmd &= ~PCI_COMMAND_VGA_PALETTE; + if (pci_dev_present(intel_82437)) { + if (!(mga_option & 0x20000000) && !minfo->devflags.nopciretry) { + printk(KERN_WARNING "matroxfb: Disabling PCI retries due to i82437 present\n"); + } + mga_option |= 0x20000000; + minfo->devflags.nopciretry = 1; + } + pci_write_config_dword(minfo->pcidev, PCI_COMMAND, cmd); + pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, mga_option); + minfo->hw.MXoptionReg = mga_option; + + /* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */ + /* maybe preinit() candidate, but it is same... for all devices... at this time... */ + pci_write_config_dword(minfo->pcidev, PCI_MGA_INDEX, 0x00003C00); + } + + err = -ENXIO; + matroxfb_read_pins(minfo); + if (minfo->hw_switch->preinit(minfo)) { + goto failVideoIO; + } + + err = -ENOMEM; + if (!matroxfb_getmemory(minfo, memsize, &minfo->video.len) || !minfo->video.len) { + printk(KERN_ERR "matroxfb: cannot determine memory size\n"); + goto failVideoIO; + } + minfo->devflags.ydstorg = 0; + + minfo->video.base = video_base_phys; + minfo->video.len_usable = minfo->video.len; + if (minfo->video.len_usable > b->base->maxdisplayable) + minfo->video.len_usable = b->base->maxdisplayable; + if (mtrr) + minfo->wc_cookie = arch_phys_wc_add(video_base_phys, + minfo->video.len); + + if (!minfo->devflags.novga) + request_region(0x3C0, 32, "matrox"); + matroxfb_g450_connect(minfo); + minfo->hw_switch->reset(minfo); + + minfo->fbcon.monspecs.hfmin = 0; + minfo->fbcon.monspecs.hfmax = fh; + minfo->fbcon.monspecs.vfmin = 0; + minfo->fbcon.monspecs.vfmax = fv; + minfo->fbcon.monspecs.dpms = 0; /* TBD */ + + /* static settings */ + vesafb_defined.red = colors[depth-1].red; + vesafb_defined.green = colors[depth-1].green; + vesafb_defined.blue = colors[depth-1].blue; + vesafb_defined.bits_per_pixel = colors[depth-1].bits_per_pixel; + vesafb_defined.grayscale = grayscale; + vesafb_defined.vmode = 0; + if (noaccel) + vesafb_defined.accel_flags &= ~FB_ACCELF_TEXT; + + minfo->fbops = matroxfb_ops; + minfo->fbcon.fbops = &minfo->fbops; + minfo->fbcon.pseudo_palette = minfo->cmap; + minfo->fbcon.flags = FBINFO_PARTIAL_PAN_OK | /* Prefer panning for scroll under MC viewer/edit */ + FBINFO_HWACCEL_COPYAREA | /* We have hw-assisted bmove */ + FBINFO_HWACCEL_FILLRECT | /* And fillrect */ + FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */ + FBINFO_HWACCEL_XPAN | /* And we support both horizontal */ + FBINFO_HWACCEL_YPAN | /* And vertical panning */ + FBINFO_READS_FAST; + minfo->video.len_usable &= PAGE_MASK; + fb_alloc_cmap(&minfo->fbcon.cmap, 256, 1); + +#ifndef MODULE + /* mode database is marked __init!!! */ + if (!hotplug) { + fb_find_mode(&vesafb_defined, &minfo->fbcon, videomode[0] ? videomode : NULL, + NULL, 0, &defaultmode, vesafb_defined.bits_per_pixel); + } +#endif /* !MODULE */ + + /* mode modifiers */ + if (hslen) + vesafb_defined.hsync_len = hslen; + if (vslen) + vesafb_defined.vsync_len = vslen; + if (left != ~0) + vesafb_defined.left_margin = left; + if (right != ~0) + vesafb_defined.right_margin = right; + if (upper != ~0) + vesafb_defined.upper_margin = upper; + if (lower != ~0) + vesafb_defined.lower_margin = lower; + if (xres) + vesafb_defined.xres = xres; + if (yres) + vesafb_defined.yres = yres; + if (sync != -1) + vesafb_defined.sync = sync; + else if (vesafb_defined.sync == ~0) { + vesafb_defined.sync = 0; + if (yres < 400) + vesafb_defined.sync |= FB_SYNC_HOR_HIGH_ACT; + else if (yres < 480) + vesafb_defined.sync |= FB_SYNC_VERT_HIGH_ACT; + } + + /* fv, fh, maxclk limits was specified */ + { + unsigned int tmp; + + if (fv) { + tmp = fv * (vesafb_defined.upper_margin + vesafb_defined.yres + + vesafb_defined.lower_margin + vesafb_defined.vsync_len); + if ((tmp < fh) || (fh == 0)) fh = tmp; + } + if (fh) { + tmp = fh * (vesafb_defined.left_margin + vesafb_defined.xres + + vesafb_defined.right_margin + vesafb_defined.hsync_len); + if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp; + } + tmp = (maxclk + 499) / 500; + if (tmp) { + tmp = (2000000000 + tmp) / tmp; + if (tmp > pixclock) pixclock = tmp; + } + } + if (pixclock) { + if (pixclock < 2000) /* > 500MHz */ + pixclock = 4000; /* 250MHz */ + if (pixclock > 1000000) + pixclock = 1000000; /* 1MHz */ + vesafb_defined.pixclock = pixclock; + } + + /* FIXME: Where to move this?! */ +#if defined(CONFIG_PPC_PMAC) +#ifndef MODULE + if (machine_is(powermac)) { + struct fb_var_screeninfo var; + + if (default_vmode <= 0 || default_vmode > VMODE_MAX) + default_vmode = VMODE_640_480_60; +#if defined(CONFIG_PPC32) + if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM) + default_cmode = nvram_read_byte(NV_CMODE); +#endif + if (default_cmode < CMODE_8 || default_cmode > CMODE_32) + default_cmode = CMODE_8; + if (!mac_vmode_to_var(default_vmode, default_cmode, &var)) { + var.accel_flags = vesafb_defined.accel_flags; + var.xoffset = var.yoffset = 0; + /* Note: mac_vmode_to_var() does not set all parameters */ + vesafb_defined = var; + } + } +#endif /* !MODULE */ +#endif /* CONFIG_PPC_PMAC */ + vesafb_defined.xres_virtual = vesafb_defined.xres; + if (nopan) { + vesafb_defined.yres_virtual = vesafb_defined.yres; + } else { + vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough + to yres_virtual * xres_virtual < 2^32 */ + } + matroxfb_init_fix(minfo); + minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase); + /* Normalize values (namely yres_virtual) */ + matroxfb_check_var(&vesafb_defined, &minfo->fbcon); + /* And put it into "current" var. Do NOT program hardware yet, or we'll not take over + * vgacon correctly. fbcon_startup will call fb_set_par for us, WITHOUT check_var, + * and unfortunately it will do it BEFORE vgacon contents is saved, so it won't work + * anyway. But we at least tried... */ + minfo->fbcon.var = vesafb_defined; + err = -EINVAL; + + printk(KERN_INFO "matroxfb: %dx%dx%dbpp (virtual: %dx%d)\n", + vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, + vesafb_defined.xres_virtual, vesafb_defined.yres_virtual); + printk(KERN_INFO "matroxfb: framebuffer at 0x%lX, mapped to 0x%p, size %d\n", + minfo->video.base, vaddr_va(minfo->video.vbase), minfo->video.len); + +/* We do not have to set currcon to 0... register_framebuffer do it for us on first console + * and we do not want currcon == 0 for subsequent framebuffers */ + + minfo->fbcon.device = &minfo->pcidev->dev; + if (register_framebuffer(&minfo->fbcon) < 0) { + goto failVideoIO; + } + fb_info(&minfo->fbcon, "%s frame buffer device\n", minfo->fbcon.fix.id); + + /* there is no console on this fb... but we have to initialize hardware + * until someone tells me what is proper thing to do */ + if (!minfo->initialized) { + fb_info(&minfo->fbcon, "initializing hardware\n"); + /* We have to use FB_ACTIVATE_FORCE, as we had to put vesafb_defined to the fbcon.var + * already before, so register_framebuffer works correctly. */ + vesafb_defined.activate |= FB_ACTIVATE_FORCE; + fb_set_var(&minfo->fbcon, &vesafb_defined); + } + + return 0; +failVideoIO:; + matroxfb_g450_shutdown(minfo); + iounmap(minfo->video.vbase.vaddr); +failCtrlIO:; + iounmap(minfo->mmio.vbase.vaddr); +failVideoMR:; + release_mem_region(video_base_phys, minfo->video.len_maximum); +failCtrlMR:; + release_mem_region(ctrlptr_phys, 16384); +fail:; + return err; +} + +static LIST_HEAD(matroxfb_list); +static LIST_HEAD(matroxfb_driver_list); + +#define matroxfb_l(x) list_entry(x, struct matrox_fb_info, next_fb) +#define matroxfb_driver_l(x) list_entry(x, struct matroxfb_driver, node) +int matroxfb_register_driver(struct matroxfb_driver* drv) { + struct matrox_fb_info* minfo; + + list_add(&drv->node, &matroxfb_driver_list); + for (minfo = matroxfb_l(matroxfb_list.next); + minfo != matroxfb_l(&matroxfb_list); + minfo = matroxfb_l(minfo->next_fb.next)) { + void* p; + + if (minfo->drivers_count == MATROXFB_MAX_FB_DRIVERS) + continue; + p = drv->probe(minfo); + if (p) { + minfo->drivers_data[minfo->drivers_count] = p; + minfo->drivers[minfo->drivers_count++] = drv; + } + } + return 0; +} + +void matroxfb_unregister_driver(struct matroxfb_driver* drv) { + struct matrox_fb_info* minfo; + + list_del(&drv->node); + for (minfo = matroxfb_l(matroxfb_list.next); + minfo != matroxfb_l(&matroxfb_list); + minfo = matroxfb_l(minfo->next_fb.next)) { + int i; + + for (i = 0; i < minfo->drivers_count; ) { + if (minfo->drivers[i] == drv) { + if (drv && drv->remove) + drv->remove(minfo, minfo->drivers_data[i]); + minfo->drivers[i] = minfo->drivers[--minfo->drivers_count]; + minfo->drivers_data[i] = minfo->drivers_data[minfo->drivers_count]; + } else + i++; + } + } +} + +static void matroxfb_register_device(struct matrox_fb_info* minfo) { + struct matroxfb_driver* drv; + int i = 0; + list_add(&minfo->next_fb, &matroxfb_list); + for (drv = matroxfb_driver_l(matroxfb_driver_list.next); + drv != matroxfb_driver_l(&matroxfb_driver_list); + drv = matroxfb_driver_l(drv->node.next)) { + if (drv->probe) { + void *p = drv->probe(minfo); + if (p) { + minfo->drivers_data[i] = p; + minfo->drivers[i++] = drv; + if (i == MATROXFB_MAX_FB_DRIVERS) + break; + } + } + } + minfo->drivers_count = i; +} + +static void matroxfb_unregister_device(struct matrox_fb_info* minfo) { + int i; + + list_del(&minfo->next_fb); + for (i = 0; i < minfo->drivers_count; i++) { + struct matroxfb_driver* drv = minfo->drivers[i]; + + if (drv && drv->remove) + drv->remove(minfo, minfo->drivers_data[i]); + } +} + +static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dummy) { + struct board* b; + u_int16_t svid; + u_int16_t sid; + struct matrox_fb_info* minfo; + int err; + u_int32_t cmd; + DBG(__func__) + + svid = pdev->subsystem_vendor; + sid = pdev->subsystem_device; + for (b = dev_list; b->vendor; b++) { + if ((b->vendor != pdev->vendor) || (b->device != pdev->device) || (b->rev < pdev->revision)) continue; + if (b->svid) + if ((b->svid != svid) || (b->sid != sid)) continue; + break; + } + /* not match... */ + if (!b->vendor) + return -ENODEV; + if (dev > 0) { + /* not requested one... */ + dev--; + return -ENODEV; + } + pci_read_config_dword(pdev, PCI_COMMAND, &cmd); + if (pci_enable_device(pdev)) { + return -1; + } + + minfo = kzalloc(sizeof(*minfo), GFP_KERNEL); + if (!minfo) + return -ENOMEM; + + minfo->pcidev = pdev; + minfo->dead = 0; + minfo->usecount = 0; + minfo->userusecount = 0; + + pci_set_drvdata(pdev, minfo); + /* DEVFLAGS */ + minfo->devflags.memtype = memtype; + if (memtype != -1) + noinit = 0; + if (cmd & PCI_COMMAND_MEMORY) { + minfo->devflags.novga = novga; + minfo->devflags.nobios = nobios; + minfo->devflags.noinit = noinit; + /* subsequent heads always needs initialization and must not enable BIOS */ + novga = 1; + nobios = 1; + noinit = 0; + } else { + minfo->devflags.novga = 1; + minfo->devflags.nobios = 1; + minfo->devflags.noinit = 0; + } + + minfo->devflags.nopciretry = no_pci_retry; + minfo->devflags.mga_24bpp_fix = inv24; + minfo->devflags.precise_width = option_precise_width; + minfo->devflags.sgram = sgram; + minfo->capable.cross4MB = cross4MB; + + spin_lock_init(&minfo->lock.DAC); + spin_lock_init(&minfo->lock.accel); + init_rwsem(&minfo->crtc2.lock); + init_rwsem(&minfo->altout.lock); + mutex_init(&minfo->fbcon.mm_lock); + minfo->irq_flags = 0; + init_waitqueue_head(&minfo->crtc1.vsync.wait); + init_waitqueue_head(&minfo->crtc2.vsync.wait); + minfo->crtc1.panpos = -1; + + err = initMatrox2(minfo, b); + if (!err) { + matroxfb_register_device(minfo); + return 0; + } + kfree(minfo); + return -1; +} + +static void pci_remove_matrox(struct pci_dev* pdev) { + struct matrox_fb_info* minfo; + + minfo = pci_get_drvdata(pdev); + matroxfb_remove(minfo, 1); +} + +static const struct pci_device_id matroxfb_devices[] = { +#ifdef CONFIG_FB_MATROX_MILLENIUM + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#endif +#ifdef CONFIG_FB_MATROX_MYSTIQUE + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#endif +#ifdef CONFIG_FB_MATROX_G + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, +#endif + {0, 0, + 0, 0, 0, 0, 0} +}; + +MODULE_DEVICE_TABLE(pci, matroxfb_devices); + + +static struct pci_driver matroxfb_driver = { + .name = "matroxfb", + .id_table = matroxfb_devices, + .probe = matroxfb_probe, + .remove = pci_remove_matrox, +}; + +/* **************************** init-time only **************************** */ + +#define RSResolution(X) ((X) & 0x0F) +#define RS640x400 1 +#define RS640x480 2 +#define RS800x600 3 +#define RS1024x768 4 +#define RS1280x1024 5 +#define RS1600x1200 6 +#define RS768x576 7 +#define RS960x720 8 +#define RS1152x864 9 +#define RS1408x1056 10 +#define RS640x350 11 +#define RS1056x344 12 /* 132 x 43 text */ +#define RS1056x400 13 /* 132 x 50 text */ +#define RS1056x480 14 /* 132 x 60 text */ +#define RSNoxNo 15 +/* 10-FF */ +static struct { int xres, yres, left, right, upper, lower, hslen, vslen, vfreq; } timmings[] __initdata = { + { 640, 400, 48, 16, 39, 8, 96, 2, 70 }, + { 640, 480, 48, 16, 33, 10, 96, 2, 60 }, + { 800, 600, 144, 24, 28, 8, 112, 6, 60 }, + { 1024, 768, 160, 32, 30, 4, 128, 4, 60 }, + { 1280, 1024, 224, 32, 32, 4, 136, 4, 60 }, + { 1600, 1200, 272, 48, 32, 5, 152, 5, 60 }, + { 768, 576, 144, 16, 28, 6, 112, 4, 60 }, + { 960, 720, 144, 24, 28, 8, 112, 4, 60 }, + { 1152, 864, 192, 32, 30, 4, 128, 4, 60 }, + { 1408, 1056, 256, 40, 32, 5, 144, 5, 60 }, + { 640, 350, 48, 16, 39, 8, 96, 2, 70 }, + { 1056, 344, 96, 24, 59, 44, 160, 2, 70 }, + { 1056, 400, 96, 24, 39, 8, 160, 2, 70 }, + { 1056, 480, 96, 24, 36, 12, 160, 3, 60 }, + { 0, 0, ~0, ~0, ~0, ~0, 0, 0, 0 } +}; + +#define RSCreate(X,Y) ((X) | ((Y) << 8)) +static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __initdata = { +/* default must be first */ + { ~0, RSCreate(RSNoxNo, RS8bpp ) }, + { 0x101, RSCreate(RS640x480, RS8bpp ) }, + { 0x100, RSCreate(RS640x400, RS8bpp ) }, + { 0x180, RSCreate(RS768x576, RS8bpp ) }, + { 0x103, RSCreate(RS800x600, RS8bpp ) }, + { 0x188, RSCreate(RS960x720, RS8bpp ) }, + { 0x105, RSCreate(RS1024x768, RS8bpp ) }, + { 0x190, RSCreate(RS1152x864, RS8bpp ) }, + { 0x107, RSCreate(RS1280x1024, RS8bpp ) }, + { 0x198, RSCreate(RS1408x1056, RS8bpp ) }, + { 0x11C, RSCreate(RS1600x1200, RS8bpp ) }, + { 0x110, RSCreate(RS640x480, RS15bpp) }, + { 0x181, RSCreate(RS768x576, RS15bpp) }, + { 0x113, RSCreate(RS800x600, RS15bpp) }, + { 0x189, RSCreate(RS960x720, RS15bpp) }, + { 0x116, RSCreate(RS1024x768, RS15bpp) }, + { 0x191, RSCreate(RS1152x864, RS15bpp) }, + { 0x119, RSCreate(RS1280x1024, RS15bpp) }, + { 0x199, RSCreate(RS1408x1056, RS15bpp) }, + { 0x11D, RSCreate(RS1600x1200, RS15bpp) }, + { 0x111, RSCreate(RS640x480, RS16bpp) }, + { 0x182, RSCreate(RS768x576, RS16bpp) }, + { 0x114, RSCreate(RS800x600, RS16bpp) }, + { 0x18A, RSCreate(RS960x720, RS16bpp) }, + { 0x117, RSCreate(RS1024x768, RS16bpp) }, + { 0x192, RSCreate(RS1152x864, RS16bpp) }, + { 0x11A, RSCreate(RS1280x1024, RS16bpp) }, + { 0x19A, RSCreate(RS1408x1056, RS16bpp) }, + { 0x11E, RSCreate(RS1600x1200, RS16bpp) }, + { 0x1B2, RSCreate(RS640x480, RS24bpp) }, + { 0x184, RSCreate(RS768x576, RS24bpp) }, + { 0x1B5, RSCreate(RS800x600, RS24bpp) }, + { 0x18C, RSCreate(RS960x720, RS24bpp) }, + { 0x1B8, RSCreate(RS1024x768, RS24bpp) }, + { 0x194, RSCreate(RS1152x864, RS24bpp) }, + { 0x1BB, RSCreate(RS1280x1024, RS24bpp) }, + { 0x19C, RSCreate(RS1408x1056, RS24bpp) }, + { 0x1BF, RSCreate(RS1600x1200, RS24bpp) }, + { 0x112, RSCreate(RS640x480, RS32bpp) }, + { 0x183, RSCreate(RS768x576, RS32bpp) }, + { 0x115, RSCreate(RS800x600, RS32bpp) }, + { 0x18B, RSCreate(RS960x720, RS32bpp) }, + { 0x118, RSCreate(RS1024x768, RS32bpp) }, + { 0x193, RSCreate(RS1152x864, RS32bpp) }, + { 0x11B, RSCreate(RS1280x1024, RS32bpp) }, + { 0x19B, RSCreate(RS1408x1056, RS32bpp) }, + { 0x11F, RSCreate(RS1600x1200, RS32bpp) }, + { 0x010, RSCreate(RS640x350, RS4bpp ) }, + { 0x012, RSCreate(RS640x480, RS4bpp ) }, + { 0x102, RSCreate(RS800x600, RS4bpp ) }, + { 0x104, RSCreate(RS1024x768, RS4bpp ) }, + { 0x106, RSCreate(RS1280x1024, RS4bpp ) }, + { 0, 0 }}; + +static void __init matroxfb_init_params(void) { + /* fh from kHz to Hz */ + if (fh < 1000) + fh *= 1000; /* 1kHz minimum */ + /* maxclk */ + if (maxclk < 1000) maxclk *= 1000; /* kHz -> Hz, MHz -> kHz */ + if (maxclk < 1000000) maxclk *= 1000; /* kHz -> Hz, 1MHz minimum */ + /* fix VESA number */ + if (vesa != ~0) + vesa &= 0x1DFF; /* mask out clearscreen, acceleration and so on */ + + /* static settings */ + for (RSptr = vesamap; RSptr->vesa; RSptr++) { + if (RSptr->vesa == vesa) break; + } + if (!RSptr->vesa) { + printk(KERN_ERR "Invalid vesa mode 0x%04X\n", vesa); + RSptr = vesamap; + } + { + int res = RSResolution(RSptr->info)-1; + if (left == ~0) + left = timmings[res].left; + if (!xres) + xres = timmings[res].xres; + if (right == ~0) + right = timmings[res].right; + if (!hslen) + hslen = timmings[res].hslen; + if (upper == ~0) + upper = timmings[res].upper; + if (!yres) + yres = timmings[res].yres; + if (lower == ~0) + lower = timmings[res].lower; + if (!vslen) + vslen = timmings[res].vslen; + if (!(fv||fh||maxclk||pixclock)) + fv = timmings[res].vfreq; + if (depth == -1) + depth = RSDepth(RSptr->info); + } +} + +static int __init matrox_init(void) { + int err; + + matroxfb_init_params(); + err = pci_register_driver(&matroxfb_driver); + dev = -1; /* accept all new devices... */ + return err; +} + +/* **************************** exit-time only **************************** */ + +static void __exit matrox_done(void) { + pci_unregister_driver(&matroxfb_driver); +} + +#ifndef MODULE + +/* ************************* init in-kernel code ************************** */ + +static int __init matroxfb_setup(char *options) { + char *this_opt; + + DBG(__func__) + + if (!options || !*options) + return 0; + + while ((this_opt = strsep(&options, ",")) != NULL) { + if (!*this_opt) continue; + + dprintk("matroxfb_setup: option %s\n", this_opt); + + if (!strncmp(this_opt, "dev:", 4)) + dev = simple_strtoul(this_opt+4, NULL, 0); + else if (!strncmp(this_opt, "depth:", 6)) { + switch (simple_strtoul(this_opt+6, NULL, 0)) { + case 0: depth = RSText; break; + case 4: depth = RS4bpp; break; + case 8: depth = RS8bpp; break; + case 15:depth = RS15bpp; break; + case 16:depth = RS16bpp; break; + case 24:depth = RS24bpp; break; + case 32:depth = RS32bpp; break; + default: + printk(KERN_ERR "matroxfb: unsupported color depth\n"); + } + } else if (!strncmp(this_opt, "xres:", 5)) + xres = simple_strtoul(this_opt+5, NULL, 0); + else if (!strncmp(this_opt, "yres:", 5)) + yres = simple_strtoul(this_opt+5, NULL, 0); + else if (!strncmp(this_opt, "vslen:", 6)) + vslen = simple_strtoul(this_opt+6, NULL, 0); + else if (!strncmp(this_opt, "hslen:", 6)) + hslen = simple_strtoul(this_opt+6, NULL, 0); + else if (!strncmp(this_opt, "left:", 5)) + left = simple_strtoul(this_opt+5, NULL, 0); + else if (!strncmp(this_opt, "right:", 6)) + right = simple_strtoul(this_opt+6, NULL, 0); + else if (!strncmp(this_opt, "upper:", 6)) + upper = simple_strtoul(this_opt+6, NULL, 0); + else if (!strncmp(this_opt, "lower:", 6)) + lower = simple_strtoul(this_opt+6, NULL, 0); + else if (!strncmp(this_opt, "pixclock:", 9)) + pixclock = simple_strtoul(this_opt+9, NULL, 0); + else if (!strncmp(this_opt, "sync:", 5)) + sync = simple_strtoul(this_opt+5, NULL, 0); + else if (!strncmp(this_opt, "vesa:", 5)) + vesa = simple_strtoul(this_opt+5, NULL, 0); + else if (!strncmp(this_opt, "maxclk:", 7)) + maxclk = simple_strtoul(this_opt+7, NULL, 0); + else if (!strncmp(this_opt, "fh:", 3)) + fh = simple_strtoul(this_opt+3, NULL, 0); + else if (!strncmp(this_opt, "fv:", 3)) + fv = simple_strtoul(this_opt+3, NULL, 0); + else if (!strncmp(this_opt, "mem:", 4)) + mem = simple_strtoul(this_opt+4, NULL, 0); + else if (!strncmp(this_opt, "mode:", 5)) + strlcpy(videomode, this_opt+5, sizeof(videomode)); + else if (!strncmp(this_opt, "outputs:", 8)) + strlcpy(outputs, this_opt+8, sizeof(outputs)); + else if (!strncmp(this_opt, "dfp:", 4)) { + dfp_type = simple_strtoul(this_opt+4, NULL, 0); + dfp = 1; + } +#ifdef CONFIG_PPC_PMAC + else if (!strncmp(this_opt, "vmode:", 6)) { + unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0); + if (vmode > 0 && vmode <= VMODE_MAX) + default_vmode = vmode; + } else if (!strncmp(this_opt, "cmode:", 6)) { + unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0); + switch (cmode) { + case 0: + case 8: + default_cmode = CMODE_8; + break; + case 15: + case 16: + default_cmode = CMODE_16; + break; + case 24: + case 32: + default_cmode = CMODE_32; + break; + } + } +#endif + else if (!strcmp(this_opt, "disabled")) /* nodisabled does not exist */ + disabled = 1; + else if (!strcmp(this_opt, "enabled")) /* noenabled does not exist */ + disabled = 0; + else if (!strcmp(this_opt, "sgram")) /* nosgram == sdram */ + sgram = 1; + else if (!strcmp(this_opt, "sdram")) + sgram = 0; + else if (!strncmp(this_opt, "memtype:", 8)) + memtype = simple_strtoul(this_opt+8, NULL, 0); + else { + int value = 1; + + if (!strncmp(this_opt, "no", 2)) { + value = 0; + this_opt += 2; + } + if (! strcmp(this_opt, "inverse")) + inverse = value; + else if (!strcmp(this_opt, "accel")) + noaccel = !value; + else if (!strcmp(this_opt, "pan")) + nopan = !value; + else if (!strcmp(this_opt, "pciretry")) + no_pci_retry = !value; + else if (!strcmp(this_opt, "vga")) + novga = !value; + else if (!strcmp(this_opt, "bios")) + nobios = !value; + else if (!strcmp(this_opt, "init")) + noinit = !value; + else if (!strcmp(this_opt, "mtrr")) + mtrr = value; + else if (!strcmp(this_opt, "inv24")) + inv24 = value; + else if (!strcmp(this_opt, "cross4MB")) + cross4MB = value; + else if (!strcmp(this_opt, "grayscale")) + grayscale = value; + else if (!strcmp(this_opt, "dfp")) + dfp = value; + else { + strlcpy(videomode, this_opt, sizeof(videomode)); + } + } + } + return 0; +} + +static int __initdata initialized = 0; + +static int __init matroxfb_init(void) +{ + char *option = NULL; + int err = 0; + + DBG(__func__) + + if (fb_get_options("matroxfb", &option)) + return -ENODEV; + matroxfb_setup(option); + + if (disabled) + return -ENXIO; + if (!initialized) { + initialized = 1; + err = matrox_init(); + } + hotplug = 1; + /* never return failure, user can hotplug matrox later... */ + return err; +} + +module_init(matroxfb_init); + +#else + +/* *************************** init module code **************************** */ + +MODULE_AUTHOR("(c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz"); +MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400/G450/G550"); +MODULE_LICENSE("GPL"); + +module_param(mem, int, 0); +MODULE_PARM_DESC(mem, "Size of available memory in MB, KB or B (2,4,8,12,16MB, default=autodetect)"); +module_param(disabled, int, 0); +MODULE_PARM_DESC(disabled, "Disabled (0 or 1=disabled) (default=0)"); +module_param(noaccel, int, 0); +MODULE_PARM_DESC(noaccel, "Do not use accelerating engine (0 or 1=disabled) (default=0)"); +module_param(nopan, int, 0); +MODULE_PARM_DESC(nopan, "Disable pan on startup (0 or 1=disabled) (default=0)"); +module_param(no_pci_retry, int, 0); +MODULE_PARM_DESC(no_pci_retry, "PCI retries enabled (0 or 1=disabled) (default=0)"); +module_param(novga, int, 0); +MODULE_PARM_DESC(novga, "VGA I/O (0x3C0-0x3DF) disabled (0 or 1=disabled) (default=0)"); +module_param(nobios, int, 0); +MODULE_PARM_DESC(nobios, "Disables ROM BIOS (0 or 1=disabled) (default=do not change BIOS state)"); +module_param(noinit, int, 0); +MODULE_PARM_DESC(noinit, "Disables W/SG/SD-RAM and bus interface initialization (0 or 1=do not initialize) (default=0)"); +module_param(memtype, int, 0); +MODULE_PARM_DESC(memtype, "Memory type for G200/G400 (see Documentation/fb/matroxfb.txt for explanation) (default=3 for G200, 0 for G400)"); +module_param(mtrr, int, 0); +MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)"); +module_param(sgram, int, 0); +MODULE_PARM_DESC(sgram, "Indicates that G100/G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)"); +module_param(inv24, int, 0); +MODULE_PARM_DESC(inv24, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)"); +module_param(inverse, int, 0); +MODULE_PARM_DESC(inverse, "Inverse (0 or 1) (default=0)"); +module_param(dev, int, 0); +MODULE_PARM_DESC(dev, "Multihead support, attach to device ID (0..N) (default=all working)"); +module_param(vesa, int, 0); +MODULE_PARM_DESC(vesa, "Startup videomode (0x000-0x1FF) (default=0x101)"); +module_param(xres, int, 0); +MODULE_PARM_DESC(xres, "Horizontal resolution (px), overrides xres from vesa (default=vesa)"); +module_param(yres, int, 0); +MODULE_PARM_DESC(yres, "Vertical resolution (scans), overrides yres from vesa (default=vesa)"); +module_param(upper, int, 0); +MODULE_PARM_DESC(upper, "Upper blank space (scans), overrides upper from vesa (default=vesa)"); +module_param(lower, int, 0); +MODULE_PARM_DESC(lower, "Lower blank space (scans), overrides lower from vesa (default=vesa)"); +module_param(vslen, int, 0); +MODULE_PARM_DESC(vslen, "Vertical sync length (scans), overrides lower from vesa (default=vesa)"); +module_param(left, int, 0); +MODULE_PARM_DESC(left, "Left blank space (px), overrides left from vesa (default=vesa)"); +module_param(right, int, 0); +MODULE_PARM_DESC(right, "Right blank space (px), overrides right from vesa (default=vesa)"); +module_param(hslen, int, 0); +MODULE_PARM_DESC(hslen, "Horizontal sync length (px), overrides hslen from vesa (default=vesa)"); +module_param(pixclock, int, 0); +MODULE_PARM_DESC(pixclock, "Pixelclock (ns), overrides pixclock from vesa (default=vesa)"); +module_param(sync, int, 0); +MODULE_PARM_DESC(sync, "Sync polarity, overrides sync from vesa (default=vesa)"); +module_param(depth, int, 0); +MODULE_PARM_DESC(depth, "Color depth (0=text,8,15,16,24,32) (default=vesa)"); +module_param(maxclk, int, 0); +MODULE_PARM_DESC(maxclk, "Startup maximal clock, 0-999MHz, 1000-999999kHz, 1000000-INF Hz"); +module_param(fh, int, 0); +MODULE_PARM_DESC(fh, "Startup horizontal frequency, 0-999kHz, 1000-INF Hz"); +module_param(fv, int, 0); +MODULE_PARM_DESC(fv, "Startup vertical frequency, 0-INF Hz\n" +"You should specify "fv:max_monitor_vsync,fh:max_monitor_hsync,maxclk:max_monitor_dotclock""); +module_param(grayscale, int, 0); +MODULE_PARM_DESC(grayscale, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)"); +module_param(cross4MB, int, 0); +MODULE_PARM_DESC(cross4MB, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)"); +module_param(dfp, int, 0); +MODULE_PARM_DESC(dfp, "Specifies whether to use digital flat panel interface of G200/G400 (0 or 1) (default=0)"); +module_param(dfp_type, int, 0); +MODULE_PARM_DESC(dfp_type, "Specifies DFP interface type (0 to 255) (default=read from hardware)"); +module_param_string(outputs, outputs, sizeof(outputs), 0); +MODULE_PARM_DESC(outputs, "Specifies which CRTC is mapped to which output (string of up to three letters, consisting of 0 (disabled), 1 (CRTC1), 2 (CRTC2)) (default=111 for Gx50, 101 for G200/G400 with DFP, and 100 for all other devices)"); +#ifdef CONFIG_PPC_PMAC +module_param_named(vmode, default_vmode, int, 0); +MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)"); +module_param_named(cmode, default_cmode, int, 0); +MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)"); +#endif + +int __init init_module(void){ + + DBG(__func__) + + if (disabled) + return -ENXIO; + + if (depth == 0) + depth = RSText; + else if (depth == 4) + depth = RS4bpp; + else if (depth == 8) + depth = RS8bpp; + else if (depth == 15) + depth = RS15bpp; + else if (depth == 16) + depth = RS16bpp; + else if (depth == 24) + depth = RS24bpp; + else if (depth == 32) + depth = RS32bpp; + else if (depth != -1) { + printk(KERN_ERR "matroxfb: depth %d is not supported, using default\n", depth); + depth = -1; + } + matrox_init(); + /* never return failure; user can hotplug matrox later... */ + return 0; +} +#endif /* MODULE */ + +module_exit(matrox_done); +EXPORT_SYMBOL(matroxfb_register_driver); +EXPORT_SYMBOL(matroxfb_unregister_driver); +EXPORT_SYMBOL(matroxfb_wait_for_sync); +EXPORT_SYMBOL(matroxfb_enable_irq); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/staging/mgakms/matroxfb_base.h b/drivers/staging/mgakms/matroxfb_base.h new file mode 100644 index 000000000000..f85ad25659e5 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_base.h @@ -0,0 +1,710 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450 + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + */ +#ifndef __MATROXFB_H__ +#define __MATROXFB_H__ + +/* general, but fairly heavy, debugging */ +#undef MATROXFB_DEBUG + +/* heavy debugging: */ +/* -- logs putc[s], so every time a char is displayed, it's logged */ +#undef MATROXFB_DEBUG_HEAVY + +/* This one _could_ cause infinite loops */ +/* It _does_ cause lots and lots of messages during idle loops */ +#undef MATROXFB_DEBUG_LOOP + +/* Debug register calls, too? */ +#undef MATROXFB_DEBUG_REG + +/* Guard accelerator accesses with spin_lock_irqsave... */ +#undef MATROXFB_USE_SPINLOCKS + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/selection.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/kd.h> + +#include <asm/io.h> +#include <asm/unaligned.h> + +#if defined(CONFIG_PPC_PMAC) +#include <asm/prom.h> +#include "../macmodes.h" +#endif + +#ifdef MATROXFB_DEBUG + +#define DEBUG +#define DBG(x) printk(KERN_DEBUG "matroxfb: %s\n", (x)); + +#ifdef MATROXFB_DEBUG_HEAVY +#define DBG_HEAVY(x) DBG(x) +#else /* MATROXFB_DEBUG_HEAVY */ +#define DBG_HEAVY(x) /* DBG_HEAVY */ +#endif /* MATROXFB_DEBUG_HEAVY */ + +#ifdef MATROXFB_DEBUG_LOOP +#define DBG_LOOP(x) DBG(x) +#else /* MATROXFB_DEBUG_LOOP */ +#define DBG_LOOP(x) /* DBG_LOOP */ +#endif /* MATROXFB_DEBUG_LOOP */ + +#ifdef MATROXFB_DEBUG_REG +#define DBG_REG(x) DBG(x) +#else /* MATROXFB_DEBUG_REG */ +#define DBG_REG(x) /* DBG_REG */ +#endif /* MATROXFB_DEBUG_REG */ + +#else /* MATROXFB_DEBUG */ + +#define DBG(x) /* DBG */ +#define DBG_HEAVY(x) /* DBG_HEAVY */ +#define DBG_REG(x) /* DBG_REG */ +#define DBG_LOOP(x) /* DBG_LOOP */ + +#endif /* MATROXFB_DEBUG */ + +#ifdef DEBUG +#define dprintk(X...) printk(X) +#else +#define dprintk(X...) +#endif + +#ifndef PCI_SS_VENDOR_ID_SIEMENS_NIXDORF +#define PCI_SS_VENDOR_ID_SIEMENS_NIXDORF 0x110A +#endif +#ifndef PCI_SS_VENDOR_ID_MATROX +#define PCI_SS_VENDOR_ID_MATROX PCI_VENDOR_ID_MATROX +#endif + +#ifndef PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP +#define PCI_SS_ID_MATROX_GENERIC 0xFF00 +#define PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP 0xFF01 +#define PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP 0xFF02 +#define PCI_SS_ID_MATROX_MILLENIUM_G200_AGP 0xFF03 +#define PCI_SS_ID_MATROX_MARVEL_G200_AGP 0xFF04 +#define PCI_SS_ID_MATROX_MGA_G100_PCI 0xFF05 +#define PCI_SS_ID_MATROX_MGA_G100_AGP 0x1001 +#define PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP 0x2179 +#define PCI_SS_ID_SIEMENS_MGA_G100_AGP 0x001E /* 30 */ +#define PCI_SS_ID_SIEMENS_MGA_G200_AGP 0x0032 /* 50 */ +#endif + +#define MX_VISUAL_TRUECOLOR FB_VISUAL_DIRECTCOLOR +#define MX_VISUAL_DIRECTCOLOR FB_VISUAL_TRUECOLOR +#define MX_VISUAL_PSEUDOCOLOR FB_VISUAL_PSEUDOCOLOR + +#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) + +/* G-series and Mystique have (almost) same DAC */ +#undef NEED_DAC1064 +#if defined(CONFIG_FB_MATROX_MYSTIQUE) || defined(CONFIG_FB_MATROX_G) +#define NEED_DAC1064 1 +#endif + +typedef struct { + void __iomem* vaddr; +} vaddr_t; + +static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) { + return readb(va.vaddr + offs); +} + +static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) { + writeb(value, va.vaddr + offs); +} + +static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) { + writew(value, va.vaddr + offs); +} + +static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) { + return readl(va.vaddr + offs); +} + +static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) { + writel(value, va.vaddr + offs); +} + +static inline void mga_memcpy_toio(vaddr_t va, const void* src, int len) { +#if defined(__alpha__) || defined(__i386__) || defined(__x86_64__) + /* + * iowrite32_rep works for us if: + * (1) Copies data as 32bit quantities, not byte after byte, + * (2) Performs LE ordered stores, and + * (3) It copes with unaligned source (destination is guaranteed to be page + * aligned and length is guaranteed to be multiple of 4). + */ + iowrite32_rep(va.vaddr, src, len >> 2); +#else + u_int32_t __iomem* addr = va.vaddr; + + if ((unsigned long)src & 3) { + while (len >= 4) { + fb_writel(get_unaligned((u32 *)src), addr); + addr++; + len -= 4; + src += 4; + } + } else { + while (len >= 4) { + fb_writel(*(u32 *)src, addr); + addr++; + len -= 4; + src += 4; + } + } +#endif +} + +static inline void vaddr_add(vaddr_t* va, unsigned long offs) { + va->vaddr += offs; +} + +static inline void __iomem* vaddr_va(vaddr_t va) { + return va.vaddr; +} + +struct my_timming { + unsigned int pixclock; + int mnp; + unsigned int crtc; + unsigned int HDisplay; + unsigned int HSyncStart; + unsigned int HSyncEnd; + unsigned int HTotal; + unsigned int VDisplay; + unsigned int VSyncStart; + unsigned int VSyncEnd; + unsigned int VTotal; + unsigned int sync; + int dblscan; + int interlaced; + unsigned int delay; /* CRTC delay */ +}; + +enum { M_SYSTEM_PLL, M_PIXEL_PLL_A, M_PIXEL_PLL_B, M_PIXEL_PLL_C, M_VIDEO_PLL }; + +struct matrox_pll_cache { + unsigned int valid; + struct { + unsigned int mnp_key; + unsigned int mnp_value; + } data[4]; +}; + +struct matrox_pll_limits { + unsigned int vcomin; + unsigned int vcomax; +}; + +struct matrox_pll_features { + unsigned int vco_freq_min; + unsigned int ref_freq; + unsigned int feed_div_min; + unsigned int feed_div_max; + unsigned int in_div_min; + unsigned int in_div_max; + unsigned int post_shift_max; +}; + +struct matroxfb_par +{ + unsigned int final_bppShift; + unsigned int cmap_len; + struct { + unsigned int bytes; + unsigned int pixels; + unsigned int chunks; + } ydstorg; +}; + +struct matrox_fb_info; + +struct matrox_DAC1064_features { + u_int8_t xvrefctrl; + u_int8_t xmiscctrl; +}; + +/* current hardware status */ +struct mavenregs { + u_int8_t regs[256]; + int mode; + int vlines; + int xtal; + int fv; + + u_int16_t htotal; + u_int16_t hcorr; +}; + +struct matrox_crtc2 { + u_int32_t ctl; +}; + +struct matrox_hw_state { + u_int32_t MXoptionReg; + unsigned char DACclk[6]; + unsigned char DACreg[80]; + unsigned char MiscOutReg; + unsigned char DACpal[768]; + unsigned char CRTC[25]; + unsigned char CRTCEXT[9]; + unsigned char SEQ[5]; + /* unused for MGA mode, but who knows... */ + unsigned char GCTL[9]; + /* unused for MGA mode, but who knows... */ + unsigned char ATTR[21]; + + /* TVOut only */ + struct mavenregs maven; + + struct matrox_crtc2 crtc2; +}; + +struct matrox_accel_data { +#ifdef CONFIG_FB_MATROX_MILLENIUM + unsigned char ramdac_rev; +#endif + u_int32_t m_dwg_rect; + u_int32_t m_opmode; + u_int32_t m_access; + u_int32_t m_pitch; +}; + +struct v4l2_queryctrl; +struct v4l2_control; + +struct matrox_altout { + const char *name; + int (*compute)(void* altout_dev, struct my_timming* input); + int (*program)(void* altout_dev); + int (*start)(void* altout_dev); + int (*verifymode)(void* altout_dev, u_int32_t mode); + int (*getqueryctrl)(void* altout_dev, + struct v4l2_queryctrl* ctrl); + int (*getctrl)(void* altout_dev, + struct v4l2_control* ctrl); + int (*setctrl)(void* altout_dev, + struct v4l2_control* ctrl); +}; + +#define MATROXFB_SRC_NONE 0 +#define MATROXFB_SRC_CRTC1 1 +#define MATROXFB_SRC_CRTC2 2 + +enum mga_chip { MGA_2064, MGA_2164, MGA_1064, MGA_1164, MGA_G100, MGA_G200, MGA_G400, MGA_G450, MGA_G550 }; + +struct matrox_bios { + unsigned int bios_valid : 1; + unsigned int pins_len; + unsigned char pins[128]; + struct { + unsigned char vMaj, vMin, vRev; + } version; + struct { + unsigned char state, tvout; + } output; +}; + +struct matrox_switch; +struct matroxfb_driver; +struct matroxfb_dh_fb_info; + +struct matrox_vsync { + wait_queue_head_t wait; + unsigned int cnt; +}; + +struct matrox_fb_info { + struct fb_info fbcon; + + struct list_head next_fb; + + int dead; + int initialized; + unsigned int usecount; + + unsigned int userusecount; + unsigned long irq_flags; + + struct matroxfb_par curr; + struct matrox_hw_state hw; + + struct matrox_accel_data accel; + + struct pci_dev* pcidev; + + struct { + struct matrox_vsync vsync; + unsigned int pixclock; + int mnp; + int panpos; + } crtc1; + struct { + struct matrox_vsync vsync; + unsigned int pixclock; + int mnp; + struct matroxfb_dh_fb_info* info; + struct rw_semaphore lock; + } crtc2; + struct { + struct rw_semaphore lock; + struct { + int brightness, contrast, saturation, hue, gamma; + int testout, deflicker; + } tvo_params; + } altout; +#define MATROXFB_MAX_OUTPUTS 3 + struct { + unsigned int src; + struct matrox_altout* output; + void* data; + unsigned int mode; + unsigned int default_src; + } outputs[MATROXFB_MAX_OUTPUTS]; + +#define MATROXFB_MAX_FB_DRIVERS 5 + struct matroxfb_driver* (drivers[MATROXFB_MAX_FB_DRIVERS]); + void* (drivers_data[MATROXFB_MAX_FB_DRIVERS]); + unsigned int drivers_count; + + struct { + unsigned long base; /* physical */ + vaddr_t vbase; /* CPU view */ + unsigned int len; + unsigned int len_usable; + unsigned int len_maximum; + } video; + + struct { + unsigned long base; /* physical */ + vaddr_t vbase; /* CPU view */ + unsigned int len; + } mmio; + + unsigned int max_pixel_clock; + unsigned int max_pixel_clock_panellink; + + struct matrox_switch* hw_switch; + + struct { + struct matrox_pll_features pll; + struct matrox_DAC1064_features DAC1064; + } features; + struct { + spinlock_t DAC; + spinlock_t accel; + } lock; + + enum mga_chip chip; + + int interleave; + int millenium; + int milleniumII; + struct { + int cfb4; + const int* vxres; + int cross4MB; + int text; + int plnwt; + int srcorg; + } capable; + int wc_cookie; + struct { + int precise_width; + int mga_24bpp_fix; + int novga; + int nobios; + int nopciretry; + int noinit; + int sgram; + int support32MB; + + int accelerator; + int text_type_aux; + int video64bits; + int crtc2; + int maven_capable; + unsigned int vgastep; + unsigned int textmode; + unsigned int textstep; + unsigned int textvram; /* character cells */ + unsigned int ydstorg; /* offset in bytes from video start to usable memory */ + /* 0 except for 6MB Millenium */ + int memtype; + int g450dac; + int dfp_type; + int panellink; /* G400 DFP possible (not G450/G550) */ + int dualhead; + unsigned int fbResource; + } devflags; + struct fb_ops fbops; + struct matrox_bios bios; + struct { + struct matrox_pll_limits pixel; + struct matrox_pll_limits system; + struct matrox_pll_limits video; + } limits; + struct { + struct matrox_pll_cache pixel; + struct matrox_pll_cache system; + struct matrox_pll_cache video; + } cache; + struct { + struct { + unsigned int video; + unsigned int system; + } pll; + struct { + u_int32_t opt; + u_int32_t opt2; + u_int32_t opt3; + u_int32_t mctlwtst; + u_int32_t mctlwtst_core; + u_int32_t memmisc; + u_int32_t memrdbk; + u_int32_t maccess; + } reg; + struct { + unsigned int ddr:1, + emrswen:1, + dll:1; + } memory; + } values; + u_int32_t cmap[16]; +}; + +#define info2minfo(info) container_of(info, struct matrox_fb_info, fbcon) + +struct matrox_switch { + int (*preinit)(struct matrox_fb_info *minfo); + void (*reset)(struct matrox_fb_info *minfo); + int (*init)(struct matrox_fb_info *minfo, struct my_timming*); + void (*restore)(struct matrox_fb_info *minfo); +}; + +struct matroxfb_driver { + struct list_head node; + char* name; + void* (*probe)(struct matrox_fb_info* info); + void (*remove)(struct matrox_fb_info* info, void* data); +}; + +int matroxfb_register_driver(struct matroxfb_driver* drv); +void matroxfb_unregister_driver(struct matroxfb_driver* drv); + +#define PCI_OPTION_REG 0x40 +#define PCI_OPTION_ENABLE_ROM 0x40000000 + +#define PCI_MGA_INDEX 0x44 +#define PCI_MGA_DATA 0x48 +#define PCI_OPTION2_REG 0x50 +#define PCI_OPTION3_REG 0x54 +#define PCI_MEMMISC_REG 0x58 + +#define M_DWGCTL 0x1C00 +#define M_MACCESS 0x1C04 +#define M_CTLWTST 0x1C08 + +#define M_PLNWT 0x1C1C + +#define M_BCOL 0x1C20 +#define M_FCOL 0x1C24 + +#define M_SGN 0x1C58 +#define M_LEN 0x1C5C +#define M_AR0 0x1C60 +#define M_AR1 0x1C64 +#define M_AR2 0x1C68 +#define M_AR3 0x1C6C +#define M_AR4 0x1C70 +#define M_AR5 0x1C74 +#define M_AR6 0x1C78 + +#define M_CXBNDRY 0x1C80 +#define M_FXBNDRY 0x1C84 +#define M_YDSTLEN 0x1C88 +#define M_PITCH 0x1C8C +#define M_YDST 0x1C90 +#define M_YDSTORG 0x1C94 +#define M_YTOP 0x1C98 +#define M_YBOT 0x1C9C + +/* mystique only */ +#define M_CACHEFLUSH 0x1FFF + +#define M_EXEC 0x0100 + +#define M_DWG_TRAP 0x04 +#define M_DWG_BITBLT 0x08 +#define M_DWG_ILOAD 0x09 + +#define M_DWG_LINEAR 0x0080 +#define M_DWG_SOLID 0x0800 +#define M_DWG_ARZERO 0x1000 +#define M_DWG_SGNZERO 0x2000 +#define M_DWG_SHIFTZERO 0x4000 + +#define M_DWG_REPLACE 0x000C0000 +#define M_DWG_REPLACE2 (M_DWG_REPLACE | 0x40) +#define M_DWG_XOR 0x00060010 + +#define M_DWG_BFCOL 0x04000000 +#define M_DWG_BMONOWF 0x08000000 + +#define M_DWG_TRANSC 0x40000000 + +#define M_FIFOSTATUS 0x1E10 +#define M_STATUS 0x1E14 +#define M_ICLEAR 0x1E18 +#define M_IEN 0x1E1C + +#define M_VCOUNT 0x1E20 + +#define M_RESET 0x1E40 +#define M_MEMRDBK 0x1E44 + +#define M_AGP2PLL 0x1E4C + +#define M_OPMODE 0x1E54 +#define M_OPMODE_DMA_GEN_WRITE 0x00 +#define M_OPMODE_DMA_BLIT 0x04 +#define M_OPMODE_DMA_VECTOR_WRITE 0x08 +#define M_OPMODE_DMA_LE 0x0000 /* little endian - no transformation */ +#define M_OPMODE_DMA_BE_8BPP 0x0000 +#define M_OPMODE_DMA_BE_16BPP 0x0100 +#define M_OPMODE_DMA_BE_32BPP 0x0200 +#define M_OPMODE_DIR_LE 0x000000 /* little endian - no transformation */ +#define M_OPMODE_DIR_BE_8BPP 0x000000 +#define M_OPMODE_DIR_BE_16BPP 0x010000 +#define M_OPMODE_DIR_BE_32BPP 0x020000 + +#define M_ATTR_INDEX 0x1FC0 +#define M_ATTR_DATA 0x1FC1 + +#define M_MISC_REG 0x1FC2 +#define M_3C2_RD 0x1FC2 + +#define M_SEQ_INDEX 0x1FC4 +#define M_SEQ_DATA 0x1FC5 +#define M_SEQ1 0x01 +#define M_SEQ1_SCROFF 0x20 + +#define M_MISC_REG_READ 0x1FCC + +#define M_GRAPHICS_INDEX 0x1FCE +#define M_GRAPHICS_DATA 0x1FCF + +#define M_CRTC_INDEX 0x1FD4 + +#define M_ATTR_RESET 0x1FDA +#define M_3DA_WR 0x1FDA +#define M_INSTS1 0x1FDA + +#define M_EXTVGA_INDEX 0x1FDE +#define M_EXTVGA_DATA 0x1FDF + +/* G200 only */ +#define M_SRCORG 0x2CB4 +#define M_DSTORG 0x2CB8 + +#define M_RAMDAC_BASE 0x3C00 + +/* fortunately, same on TVP3026 and MGA1064 */ +#define M_DAC_REG (M_RAMDAC_BASE+0) +#define M_DAC_VAL (M_RAMDAC_BASE+1) +#define M_PALETTE_MASK (M_RAMDAC_BASE+2) + +#define M_X_INDEX 0x00 +#define M_X_DATAREG 0x0A + +#define DAC_XGENIOCTRL 0x2A +#define DAC_XGENIODATA 0x2B + +#define M_C2CTL 0x3C10 + +#define MX_OPTION_BSWAP 0x00000000 + +#ifdef __LITTLE_ENDIAN +#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) +#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) +#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) +#define M_OPMODE_24BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) +#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) +#else +#ifdef __BIG_ENDIAN +#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */ +#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) +#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT) +#define M_OPMODE_24BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */ +#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT) +#else +#error "Byte ordering have to be defined. Cannot continue." +#endif +#endif + +#define mga_inb(addr) mga_readb(minfo->mmio.vbase, (addr)) +#define mga_inl(addr) mga_readl(minfo->mmio.vbase, (addr)) +#define mga_outb(addr,val) mga_writeb(minfo->mmio.vbase, (addr), (val)) +#define mga_outw(addr,val) mga_writew(minfo->mmio.vbase, (addr), (val)) +#define mga_outl(addr,val) mga_writel(minfo->mmio.vbase, (addr), (val)) +#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1)) +#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port)) + +#define mga_fifo(n) do {} while ((mga_inl(M_FIFOSTATUS) & 0xFF) < (n)) + +#define WaitTillIdle() do { mga_inl(M_STATUS); do {} while (mga_inl(M_STATUS) & 0x10000); } while (0) + +/* code speedup */ +#ifdef CONFIG_FB_MATROX_MILLENIUM +#define isInterleave(x) (x->interleave) +#define isMillenium(x) (x->millenium) +#define isMilleniumII(x) (x->milleniumII) +#else +#define isInterleave(x) (0) +#define isMillenium(x) (0) +#define isMilleniumII(x) (0) +#endif + +#define matroxfb_DAC_lock() spin_lock(&minfo->lock.DAC) +#define matroxfb_DAC_unlock() spin_unlock(&minfo->lock.DAC) +#define matroxfb_DAC_lock_irqsave(flags) spin_lock_irqsave(&minfo->lock.DAC, flags) +#define matroxfb_DAC_unlock_irqrestore(flags) spin_unlock_irqrestore(&minfo->lock.DAC, flags) +extern void matroxfb_DAC_out(const struct matrox_fb_info *minfo, int reg, + int val); +extern int matroxfb_DAC_in(const struct matrox_fb_info *minfo, int reg); +extern void matroxfb_var2my(struct fb_var_screeninfo* fvsi, struct my_timming* mt); +extern int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc); +extern int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable); + +#ifdef MATROXFB_USE_SPINLOCKS +#define CRITBEGIN spin_lock_irqsave(&minfo->lock.accel, critflags); +#define CRITEND spin_unlock_irqrestore(&minfo->lock.accel, critflags); +#define CRITFLAGS unsigned long critflags; +#else +#define CRITBEGIN +#define CRITEND +#define CRITFLAGS +#endif + +#endif /* __MATROXFB_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_crtc2.h b/drivers/staging/mgakms/matroxfb_crtc2.h new file mode 100644 index 000000000000..23e90e210905 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_crtc2.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_CRTC2_H__ +#define __MATROXFB_CRTC2_H__ + +#include <linux/ioctl.h> +#include "matroxfb_base.h" + +struct matroxfb_dh_fb_info { + struct fb_info fbcon; + int fbcon_registered; + int initialized; + + struct matrox_fb_info* primary_dev; + + struct { + unsigned long base; /* physical */ + vaddr_t vbase; /* virtual */ + unsigned int len; + unsigned int len_usable; + unsigned int len_maximum; + unsigned int offbase; + unsigned int borrowed; + } video; + struct { + unsigned long base; + vaddr_t vbase; + unsigned int len; + } mmio; + + unsigned int interlaced:1; + + u_int32_t cmap[16]; +}; + +#endif /* __MATROXFB_CRTC2_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_g450.c b/drivers/staging/mgakms/matroxfb_g450.c new file mode 100644 index 000000000000..f108ae66fc83 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_g450.c @@ -0,0 +1,640 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.65 2002/08/14 + * + * See matroxfb_base.c for contributors. + * + */ + +#include "matroxfb_base.h" +#include "matroxfb_misc.h" +#include "matroxfb_DAC1064.h" +#include "g450_pll.h" +#include <linux/matroxfb.h> +#include <asm/div64.h> + +#include "matroxfb_g450.h" + +/* Definition of the various controls */ +struct mctl { + struct v4l2_queryctrl desc; + size_t control; +}; + +#define BLMIN 0xF3 +#define WLMAX 0x3FF + +static const struct mctl g450_controls[] = +{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, + "brightness", + 0, WLMAX-BLMIN, 1, 370-BLMIN, + 0, + }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) }, + { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, + "contrast", + 0, 1023, 1, 127, + 0, + }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) }, + { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, + "saturation", + 0, 255, 1, 165, + 0, + }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) }, + { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, + "hue", + 0, 255, 1, 0, + 0, + }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, + { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, + "test output", + 0, 1, 1, 0, + 0, + }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, +}; + +#define G450CTRLS ARRAY_SIZE(g450_controls) + +/* Return: positive number: id found + -EINVAL: id not found, return failure + -ENOENT: id not found, create fake disabled control */ +static int get_ctrl_id(__u32 v4l2_id) { + int i; + + for (i = 0; i < G450CTRLS; i++) { + if (v4l2_id < g450_controls[i].desc.id) { + if (g450_controls[i].desc.id == 0x08000000) { + return -EINVAL; + } + return -ENOENT; + } + if (v4l2_id == g450_controls[i].desc.id) { + return i; + } + } + return -EINVAL; +} + +static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx) +{ + return (int*)((char*)minfo + g450_controls[idx].control); +} + +static void tvo_fill_defaults(struct matrox_fb_info *minfo) +{ + unsigned int i; + + for (i = 0; i < G450CTRLS; i++) { + *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value; + } +} + +static int cve2_get_reg(struct matrox_fb_info *minfo, int reg) +{ + unsigned long flags; + int val; + + matroxfb_DAC_lock_irqsave(flags); + matroxfb_DAC_out(minfo, 0x87, reg); + val = matroxfb_DAC_in(minfo, 0x88); + matroxfb_DAC_unlock_irqrestore(flags); + return val; +} + +static void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val) +{ + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + matroxfb_DAC_out(minfo, 0x87, reg); + matroxfb_DAC_out(minfo, 0x88, val); + matroxfb_DAC_unlock_irqrestore(flags); +} + +static void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val) +{ + unsigned long flags; + + matroxfb_DAC_lock_irqsave(flags); + matroxfb_DAC_out(minfo, 0x87, reg); + matroxfb_DAC_out(minfo, 0x88, val >> 2); + matroxfb_DAC_out(minfo, 0x87, reg + 1); + matroxfb_DAC_out(minfo, 0x88, val & 3); + matroxfb_DAC_unlock_irqrestore(flags); +} + +static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl, + int *wl) +{ + const int b = minfo->altout.tvo_params.brightness + BLMIN; + const int c = minfo->altout.tvo_params.contrast; + + *bl = max(b - c, BLMIN); + *wl = min(b + c, WLMAX); +} + +static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) { + int i; + + i = get_ctrl_id(p->id); + if (i >= 0) { + *p = g450_controls[i].desc; + return 0; + } + if (i == -ENOENT) { + static const struct v4l2_queryctrl disctrl = + { .flags = V4L2_CTRL_FLAG_DISABLED }; + + i = p->id; + *p = disctrl; + p->id = i; + sprintf(p->name, "Ctrl #%08X", i); + return 0; + } + return -EINVAL; +} + +static int g450_set_ctrl(void* md, struct v4l2_control *p) { + int i; + struct matrox_fb_info *minfo = md; + + i = get_ctrl_id(p->id); + if (i < 0) return -EINVAL; + + /* + * Check if changed. + */ + if (p->value == *get_ctrl_ptr(minfo, i)) return 0; + + /* + * Check limits. + */ + if (p->value > g450_controls[i].desc.maximum) return -EINVAL; + if (p->value < g450_controls[i].desc.minimum) return -EINVAL; + + /* + * Store new value. + */ + *get_ctrl_ptr(minfo, i) = p->value; + + switch (p->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + { + int blacklevel, whitelevel; + g450_compute_bwlevel(minfo, &blacklevel, &whitelevel); + cve2_set_reg10(minfo, 0x0e, blacklevel); + cve2_set_reg10(minfo, 0x1e, whitelevel); + } + break; + case V4L2_CID_SATURATION: + cve2_set_reg(minfo, 0x20, p->value); + cve2_set_reg(minfo, 0x22, p->value); + break; + case V4L2_CID_HUE: + cve2_set_reg(minfo, 0x25, p->value); + break; + case MATROXFB_CID_TESTOUT: + { + unsigned char val = cve2_get_reg(minfo, 0x05); + if (p->value) val |= 0x02; + else val &= ~0x02; + cve2_set_reg(minfo, 0x05, val); + } + break; + } + + + return 0; +} + +static int g450_get_ctrl(void* md, struct v4l2_control *p) { + int i; + struct matrox_fb_info *minfo = md; + + i = get_ctrl_id(p->id); + if (i < 0) return -EINVAL; + p->value = *get_ctrl_ptr(minfo, i); + return 0; +} + +struct output_desc { + unsigned int h_vis; + unsigned int h_f_porch; + unsigned int h_sync; + unsigned int h_b_porch; + unsigned long long int chromasc; + unsigned int burst; + unsigned int v_total; +}; + +static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r, + struct my_timming *mt, const struct output_desc *outd) +{ + u_int32_t chromasc; + u_int32_t hlen; + u_int32_t hsl; + u_int32_t hbp; + u_int32_t hfp; + u_int32_t hvis; + unsigned int pixclock; + unsigned long long piic; + int mnp; + int over; + + r->regs[0x80] = 0x03; /* | 0x40 for SCART */ + + hvis = ((mt->HDisplay << 1) + 3) & ~3; + + if (hvis >= 2048) { + hvis = 2044; + } + + piic = 1000000000ULL * hvis; + do_div(piic, outd->h_vis); + + dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic); + + mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL); + + mt->mnp = mnp; + mt->pixclock = g450_mnp2f(minfo, mnp); + + dprintk(KERN_DEBUG "MNP=%08X\n", mnp); + + pixclock = 1000000000U / mt->pixclock; + + dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock); + + piic = outd->chromasc; + do_div(piic, mt->pixclock); + chromasc = piic; + + dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc); + + r->regs[0] = piic >> 24; + r->regs[1] = piic >> 16; + r->regs[2] = piic >> 8; + r->regs[3] = piic >> 0; + hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1; + hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1; + hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1; + hlen = hvis + hfp + hsl + hbp; + over = hlen & 0x0F; + + dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen); + + if (over) { + hfp -= over; + hlen -= over; + if (over <= 2) { + } else if (over < 10) { + hfp += 4; + hlen += 4; + } else { + hfp += 16; + hlen += 16; + } + } + + /* maybe cve2 has requirement 800 < hlen < 1184 */ + r->regs[0x08] = hsl; + r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock; /* burst length */ + r->regs[0x0A] = hbp; + r->regs[0x2C] = hfp; + r->regs[0x31] = hvis / 8; + r->regs[0x32] = hvis & 7; + + dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen); + + r->regs[0x84] = 1; /* x sync point */ + r->regs[0x85] = 0; + hvis = hvis >> 1; + hlen = hlen >> 1; + + dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis); + + mt->interlaced = 1; + + mt->HDisplay = hvis & ~7; + mt->HSyncStart = mt->HDisplay + 8; + mt->HSyncEnd = (hlen & ~7) - 8; + mt->HTotal = hlen; + + { + int upper; + unsigned int vtotal; + unsigned int vsyncend; + unsigned int vdisplay; + + vtotal = mt->VTotal; + vsyncend = mt->VSyncEnd; + vdisplay = mt->VDisplay; + if (vtotal < outd->v_total) { + unsigned int yovr = outd->v_total - vtotal; + + vsyncend += yovr >> 1; + } else if (vtotal > outd->v_total) { + vdisplay = outd->v_total - 4; + vsyncend = outd->v_total; + } + upper = (outd->v_total - vsyncend) >> 1; /* in field lines */ + r->regs[0x17] = outd->v_total / 4; + r->regs[0x18] = outd->v_total & 3; + r->regs[0x33] = upper - 1; /* upper blanking */ + r->regs[0x82] = upper; /* y sync point */ + r->regs[0x83] = upper >> 8; + + mt->VDisplay = vdisplay; + mt->VSyncStart = outd->v_total - 2; + mt->VSyncEnd = outd->v_total; + mt->VTotal = outd->v_total; + } +} + +static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) { + static const struct output_desc paloutd = { + .h_vis = 52148148, // ps + .h_f_porch = 1407407, // ps + .h_sync = 4666667, // ps + .h_b_porch = 5777778, // ps + .chromasc = 19042247534182ULL, // 4433618.750 Hz + .burst = 2518518, // ps + .v_total = 625, + }; + static const struct output_desc ntscoutd = { + .h_vis = 52888889, // ps + .h_f_porch = 1333333, // ps + .h_sync = 4666667, // ps + .h_b_porch = 4666667, // ps + .chromasc = 15374030659475ULL, // 3579545.454 Hz + .burst = 2418418, // ps + .v_total = 525, // lines + }; + + static const struct mavenregs palregs = { { + 0x2A, 0x09, 0x8A, 0xCB, /* 00: chroma subcarrier */ + 0x00, + 0x00, /* test */ + 0xF9, /* modified by code (F9 written...) */ + 0x00, /* ? not written */ + 0x7E, /* 08 */ + 0x44, /* 09 */ + 0x9C, /* 0A */ + 0x2E, /* 0B */ + 0x21, /* 0C */ + 0x00, /* ? not written */ +// 0x3F, 0x03, /* 0E-0F */ + 0x3C, 0x03, + 0x3C, 0x03, /* 10-11 */ + 0x1A, /* 12 */ + 0x2A, /* 13 */ + 0x1C, 0x3D, 0x14, /* 14-16 */ + 0x9C, 0x01, /* 17-18 */ + 0x00, /* 19 */ + 0xFE, /* 1A */ + 0x7E, /* 1B */ + 0x60, /* 1C */ + 0x05, /* 1D */ +// 0x89, 0x03, /* 1E-1F */ + 0xAD, 0x03, +// 0x72, /* 20 */ + 0xA5, + 0x07, /* 21 */ +// 0x72, /* 22 */ + 0xA5, + 0x00, /* 23 */ + 0x00, /* 24 */ + 0x00, /* 25 */ + 0x08, /* 26 */ + 0x04, /* 27 */ + 0x00, /* 28 */ + 0x1A, /* 29 */ + 0x55, 0x01, /* 2A-2B */ + 0x26, /* 2C */ + 0x07, 0x7E, /* 2D-2E */ + 0x02, 0x54, /* 2F-30 */ + 0xB0, 0x00, /* 31-32 */ + 0x14, /* 33 */ + 0x49, /* 34 */ + 0x00, /* 35 written multiple times */ + 0x00, /* 36 not written */ + 0xA3, /* 37 */ + 0xC8, /* 38 */ + 0x22, /* 39 */ + 0x02, /* 3A */ + 0x22, /* 3B */ + 0x3F, 0x03, /* 3C-3D */ + 0x00, /* 3E written multiple times */ + 0x00, /* 3F not written */ + } }; + static const struct mavenregs ntscregs = { { + 0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */ + 0x00, + 0x00, /* test */ + 0xF9, /* modified by code (F9 written...) */ + 0x00, /* ? not written */ + 0x7E, /* 08 */ + 0x43, /* 09 */ + 0x7E, /* 0A */ + 0x3D, /* 0B */ + 0x00, /* 0C */ + 0x00, /* ? not written */ + 0x41, 0x00, /* 0E-0F */ + 0x3C, 0x00, /* 10-11 */ + 0x17, /* 12 */ + 0x21, /* 13 */ + 0x1B, 0x1B, 0x24, /* 14-16 */ + 0x83, 0x01, /* 17-18 */ + 0x00, /* 19 */ + 0x0F, /* 1A */ + 0x0F, /* 1B */ + 0x60, /* 1C */ + 0x05, /* 1D */ + //0x89, 0x02, /* 1E-1F */ + 0xC0, 0x02, /* 1E-1F */ + //0x5F, /* 20 */ + 0x9C, /* 20 */ + 0x04, /* 21 */ + //0x5F, /* 22 */ + 0x9C, /* 22 */ + 0x01, /* 23 */ + 0x02, /* 24 */ + 0x00, /* 25 */ + 0x0A, /* 26 */ + 0x05, /* 27 */ + 0x00, /* 28 */ + 0x10, /* 29 */ + 0xFF, 0x03, /* 2A-2B */ + 0x24, /* 2C */ + 0x0F, 0x78, /* 2D-2E */ + 0x00, 0x00, /* 2F-30 */ + 0xB2, 0x04, /* 31-32 */ + 0x14, /* 33 */ + 0x02, /* 34 */ + 0x00, /* 35 written multiple times */ + 0x00, /* 36 not written */ + 0xA3, /* 37 */ + 0xC8, /* 38 */ + 0x15, /* 39 */ + 0x05, /* 3A */ + 0x3B, /* 3B */ + 0x3C, 0x00, /* 3C-3D */ + 0x00, /* 3E written multiple times */ + 0x00, /* never written */ + } }; + + if (norm == MATROXFB_OUTPUT_MODE_PAL) { + *data = palregs; + *outd = &paloutd; + } else { + *data = ntscregs; + *outd = &ntscoutd; + } + return; +} + +#define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)]) +static void cve2_init_TV(struct matrox_fb_info *minfo, + const struct mavenregs *m) +{ + int i; + + LR(0x80); + LR(0x82); LR(0x83); + LR(0x84); LR(0x85); + + cve2_set_reg(minfo, 0x3E, 0x01); + + for (i = 0; i < 0x3E; i++) { + LR(i); + } + cve2_set_reg(minfo, 0x3E, 0x00); +} + +static int matroxfb_g450_compute(void* md, struct my_timming* mt) { + struct matrox_fb_info *minfo = md; + + dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode); + + if (mt->crtc == MATROXFB_SRC_CRTC2 && + minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { + const struct output_desc* outd; + + cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd); + { + int blacklevel, whitelevel; + g450_compute_bwlevel(minfo, &blacklevel, &whitelevel); + minfo->hw.maven.regs[0x0E] = blacklevel >> 2; + minfo->hw.maven.regs[0x0F] = blacklevel & 3; + minfo->hw.maven.regs[0x1E] = whitelevel >> 2; + minfo->hw.maven.regs[0x1F] = whitelevel & 3; + + minfo->hw.maven.regs[0x20] = + minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation; + + minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue; + + if (minfo->altout.tvo_params.testout) { + minfo->hw.maven.regs[0x05] |= 0x02; + } + } + computeRegs(minfo, &minfo->hw.maven, mt, outd); + } else if (mt->mnp < 0) { + /* We must program clocks before CRTC2, otherwise interlaced mode + startup may fail */ + mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + mt->pixclock = g450_mnp2f(minfo, mt->mnp); + } + dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock); + return 0; +} + +static int matroxfb_g450_program(void* md) { + struct matrox_fb_info *minfo = md; + + if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { + cve2_init_TV(minfo, &minfo->hw.maven); + } + return 0; +} + +static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) { + switch (arg) { + case MATROXFB_OUTPUT_MODE_PAL: + case MATROXFB_OUTPUT_MODE_NTSC: + case MATROXFB_OUTPUT_MODE_MONITOR: + return 0; + } + return -EINVAL; +} + +static int g450_dvi_compute(void* md, struct my_timming* mt) { + struct matrox_fb_info *minfo = md; + + if (mt->mnp < 0) { + mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); + mt->pixclock = g450_mnp2f(minfo, mt->mnp); + } + return 0; +} + +static struct matrox_altout matroxfb_g450_altout = { + .name = "Secondary output", + .compute = matroxfb_g450_compute, + .program = matroxfb_g450_program, + .verifymode = matroxfb_g450_verify_mode, + .getqueryctrl = g450_query_ctrl, + .getctrl = g450_get_ctrl, + .setctrl = g450_set_ctrl, +}; + +static struct matrox_altout matroxfb_g450_dvi = { + .name = "DVI output", + .compute = g450_dvi_compute, +}; + +void matroxfb_g450_connect(struct matrox_fb_info *minfo) +{ + if (minfo->devflags.g450dac) { + down_write(&minfo->altout.lock); + tvo_fill_defaults(minfo); + minfo->outputs[1].src = minfo->outputs[1].default_src; + minfo->outputs[1].data = minfo; + minfo->outputs[1].output = &matroxfb_g450_altout; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[2].src = minfo->outputs[2].default_src; + minfo->outputs[2].data = minfo; + minfo->outputs[2].output = &matroxfb_g450_dvi; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); + } +} + +void matroxfb_g450_shutdown(struct matrox_fb_info *minfo) +{ + if (minfo->devflags.g450dac) { + down_write(&minfo->altout.lock); + minfo->outputs[1].src = MATROXFB_SRC_NONE; + minfo->outputs[1].output = NULL; + minfo->outputs[1].data = NULL; + minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; + minfo->outputs[2].src = MATROXFB_SRC_NONE; + minfo->outputs[2].output = NULL; + minfo->outputs[2].data = NULL; + minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR; + up_write(&minfo->altout.lock); + } +} + +EXPORT_SYMBOL(matroxfb_g450_connect); +EXPORT_SYMBOL(matroxfb_g450_shutdown); + +MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec vandrove@vc.cvut.cz"); +MODULE_DESCRIPTION("Matrox G450/G550 output driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_g450.h b/drivers/staging/mgakms/matroxfb_g450.h new file mode 100644 index 000000000000..b5f17b86eae5 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_g450.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_G450_H__ +#define __MATROXFB_G450_H__ + +#include "matroxfb_base.h" + +#ifdef CONFIG_FB_MATROX_G +void matroxfb_g450_connect(struct matrox_fb_info *minfo); +void matroxfb_g450_shutdown(struct matrox_fb_info *minfo); +#else +static inline void matroxfb_g450_connect(struct matrox_fb_info *minfo) { }; +static inline void matroxfb_g450_shutdown(struct matrox_fb_info *minfo) { }; +#endif + +#endif /* __MATROXFB_G450_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_maven.h b/drivers/staging/mgakms/matroxfb_maven.h new file mode 100644 index 000000000000..f896b78836b1 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_maven.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_MAVEN_H__ +#define __MATROXFB_MAVEN_H__ + +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include "matroxfb_base.h" + +struct i2c_bit_adapter { + struct i2c_adapter adapter; + int initialized; + struct i2c_algo_bit_data bac; + struct matrox_fb_info* minfo; + struct { + unsigned int data; + unsigned int clock; + } mask; +}; + +#endif /* __MATROXFB_MAVEN_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_misc.c b/drivers/staging/mgakms/matroxfb_misc.c new file mode 100644 index 000000000000..9948ca2a3046 --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_misc.c @@ -0,0 +1,815 @@ +/* + * + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 + * + * (c) 1998-2002 Petr Vandrovec vandrove@vc.cvut.cz + * + * Portions Copyright (c) 2001 Matrox Graphics Inc. + * + * Version: 1.65 2002/08/14 + * + * MTRR stuff: 1998 Tom Rini trini@kernel.crashing.org + * + * Contributors: "menion?" menion@mindless.com + * Betatesting, fixes, ideas + * + * "Kurt Garloff" garloff@suse.de + * Betatesting, fixes, ideas, videomodes, videomodes timmings + * + * "Tom Rini" trini@kernel.crashing.org + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas + * + * "Bibek Sahu" scorpio@dodds.net + * Access device through readb|w|l and write b|w|l + * Extensive debugging stuff + * + * "Daniel Haun" haund@usa.net + * Testing, hardware cursor fixes + * + * "Scott Wood" sawst46+@pitt.edu + * Fixes + * + * "Gerd Knorr" kraxel@goldbach.isdn.cs.tu-berlin.de + * Betatesting + * + * "Kelly French" targon@hazmat.com + * "Fernando Herrera" fherrera@eurielec.etsit.upm.es + * Betatesting, bug reporting + * + * "Pablo Bianucci" pbian@pccp.com.ar + * Fixes, ideas, betatesting + * + * "Inaky Perez Gonzalez" inaky@peloncho.fis.ucm.es + * Fixes, enhandcements, ideas, betatesting + * + * "Ryuichi Oikawa" roikawa@rr.iiij4u.or.jp + * PPC betatesting, PPC support, backward compatibility + * + * "Paul Womar" Paul@pwomar.demon.co.uk + * "Owen Waller" O.Waller@ee.qub.ac.uk + * PPC betatesting + * + * "Thomas Pornin" pornin@bolet.ens.fr + * Alpha betatesting + * + * "Pieter van Leuven" pvl@iae.nl + * "Ulf Jaenicke-Roessler" ujr@physik.phy.tu-dresden.de + * G100 testing + * + * "H. Peter Arvin" hpa@transmeta.com + * Ideas + * + * "Cort Dougan" cort@cs.nmt.edu + * CHRP fixes and PReP cleanup + * + * "Mark Vojkovich" mvojkovi@ucsd.edu + * G400 support + * + * "David C. Hansen" haveblue@us.ibm.com + * Fixes + * + * "Ian Romanick" idr@us.ibm.com + * Find PInS data in BIOS on PowerPC systems. + * + * (following author is not in any relation with this code, but his code + * is included in this driver) + * + * Based on framebuffer driver for VBE 2.0 compliant graphic boards + * (c) 1998 Gerd Knorr kraxel@cs.tu-berlin.de + * + * (following author is not in any relation with this code, but his ideas + * were used when writing this driver) + * + * FreeVBE/AF (Matrox), "Shawn Hargreaves" shawn@talula.demon.co.uk + * + */ + + +#include "matroxfb_misc.h" +#include <linux/interrupt.h> +#include <linux/matroxfb.h> + +void matroxfb_DAC_out(const struct matrox_fb_info *minfo, int reg, int val) +{ + DBG_REG(__func__) + mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); + mga_outb(M_RAMDAC_BASE+M_X_DATAREG, val); +} + +int matroxfb_DAC_in(const struct matrox_fb_info *minfo, int reg) +{ + DBG_REG(__func__) + mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg); + return mga_inb(M_RAMDAC_BASE+M_X_DATAREG); +} + +void matroxfb_var2my(struct fb_var_screeninfo* var, struct my_timming* mt) { + unsigned int pixclock = var->pixclock; + + DBG(__func__) + + if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */ + mt->pixclock = 1000000000 / pixclock; + if (mt->pixclock < 1) mt->pixclock = 1; + mt->mnp = -1; + mt->dblscan = var->vmode & FB_VMODE_DOUBLE; + mt->interlaced = var->vmode & FB_VMODE_INTERLACED; + mt->HDisplay = var->xres; + mt->HSyncStart = mt->HDisplay + var->right_margin; + mt->HSyncEnd = mt->HSyncStart + var->hsync_len; + mt->HTotal = mt->HSyncEnd + var->left_margin; + mt->VDisplay = var->yres; + mt->VSyncStart = mt->VDisplay + var->lower_margin; + mt->VSyncEnd = mt->VSyncStart + var->vsync_len; + mt->VTotal = mt->VSyncEnd + var->upper_margin; + mt->sync = var->sync; +} + +int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax, + unsigned int* in, unsigned int* feed, unsigned int* post) { + unsigned int bestdiff = ~0; + unsigned int bestvco = 0; + unsigned int fxtal = pll->ref_freq; + unsigned int fwant; + unsigned int p; + + DBG(__func__) + + fwant = freq; + +#ifdef DEBUG + printk(KERN_ERR "post_shift_max: %d\n", pll->post_shift_max); + printk(KERN_ERR "ref_freq: %d\n", pll->ref_freq); + printk(KERN_ERR "freq: %d\n", freq); + printk(KERN_ERR "vco_freq_min: %d\n", pll->vco_freq_min); + printk(KERN_ERR "in_div_min: %d\n", pll->in_div_min); + printk(KERN_ERR "in_div_max: %d\n", pll->in_div_max); + printk(KERN_ERR "feed_div_min: %d\n", pll->feed_div_min); + printk(KERN_ERR "feed_div_max: %d\n", pll->feed_div_max); + printk(KERN_ERR "fmax: %d\n", fmax); +#endif + for (p = 1; p <= pll->post_shift_max; p++) { + if (fwant * 2 > fmax) + break; + fwant *= 2; + } + if (fwant < pll->vco_freq_min) fwant = pll->vco_freq_min; + if (fwant > fmax) fwant = fmax; + for (; p-- > 0; fwant >>= 1, bestdiff >>= 1) { + unsigned int m; + + if (fwant < pll->vco_freq_min) break; + for (m = pll->in_div_min; m <= pll->in_div_max; m++) { + unsigned int diff, fvco; + unsigned int n; + + n = (fwant * (m + 1) + (fxtal >> 1)) / fxtal - 1; + if (n > pll->feed_div_max) + break; + if (n < pll->feed_div_min) + n = pll->feed_div_min; + fvco = (fxtal * (n + 1)) / (m + 1); + if (fvco < fwant) + diff = fwant - fvco; + else + diff = fvco - fwant; + if (diff < bestdiff) { + bestdiff = diff; + *post = p; + *in = m; + *feed = n; + bestvco = fvco; + } + } + } + dprintk(KERN_ERR "clk: %02X %02X %02X %d %d %d\n", *in, *feed, *post, fxtal, bestvco, fwant); + return bestvco; +} + +int matroxfb_vgaHWinit(struct matrox_fb_info *minfo, struct my_timming *m) +{ + unsigned int hd, hs, he, hbe, ht; + unsigned int vd, vs, ve, vt, lc; + unsigned int wd; + unsigned int divider; + int i; + struct matrox_hw_state * const hw = &minfo->hw; + + DBG(__func__) + + hw->SEQ[0] = 0x00; + hw->SEQ[1] = 0x01; /* or 0x09 */ + hw->SEQ[2] = 0x0F; /* bitplanes */ + hw->SEQ[3] = 0x00; + hw->SEQ[4] = 0x0E; + /* CRTC 0..7, 9, 16..19, 21, 22 are reprogrammed by Matrox Millennium code... Hope that by MGA1064 too */ + if (m->dblscan) { + m->VTotal <<= 1; + m->VDisplay <<= 1; + m->VSyncStart <<= 1; + m->VSyncEnd <<= 1; + } + if (m->interlaced) { + m->VTotal >>= 1; + m->VDisplay >>= 1; + m->VSyncStart >>= 1; + m->VSyncEnd >>= 1; + } + + /* GCTL is ignored when not using 0xA0000 aperture */ + hw->GCTL[0] = 0x00; + hw->GCTL[1] = 0x00; + hw->GCTL[2] = 0x00; + hw->GCTL[3] = 0x00; + hw->GCTL[4] = 0x00; + hw->GCTL[5] = 0x40; + hw->GCTL[6] = 0x05; + hw->GCTL[7] = 0x0F; + hw->GCTL[8] = 0xFF; + + /* Whole ATTR is ignored in PowerGraphics mode */ + for (i = 0; i < 16; i++) + hw->ATTR[i] = i; + hw->ATTR[16] = 0x41; + hw->ATTR[17] = 0xFF; + hw->ATTR[18] = 0x0F; + hw->ATTR[19] = 0x00; + hw->ATTR[20] = 0x00; + + hd = m->HDisplay >> 3; + hs = m->HSyncStart >> 3; + he = m->HSyncEnd >> 3; + ht = m->HTotal >> 3; + /* standard timmings are in 8pixels, but for interleaved we cannot */ + /* do it for 4bpp (because of (4bpp >> 1(interleaved))/4 == 0) */ + /* using 16 or more pixels per unit can save us */ + divider = minfo->curr.final_bppShift; + while (divider & 3) { + hd >>= 1; + hs >>= 1; + he >>= 1; + ht >>= 1; + divider <<= 1; + } + divider = divider / 4; + /* divider can be from 1 to 8 */ + while (divider > 8) { + hd <<= 1; + hs <<= 1; + he <<= 1; + ht <<= 1; + divider >>= 1; + } + hd = hd - 1; + hs = hs - 1; + he = he - 1; + ht = ht - 1; + vd = m->VDisplay - 1; + vs = m->VSyncStart - 1; + ve = m->VSyncEnd - 1; + vt = m->VTotal - 2; + lc = vd; + /* G200 cannot work with (ht & 7) == 6 */ + if (((ht & 0x07) == 0x06) || ((ht & 0x0F) == 0x04)) + ht++; + hbe = ht; + wd = minfo->fbcon.var.xres_virtual * minfo->curr.final_bppShift / 64; + + hw->CRTCEXT[0] = 0; + hw->CRTCEXT[5] = 0; + if (m->interlaced) { + hw->CRTCEXT[0] = 0x80; + hw->CRTCEXT[5] = (hs + he - ht) >> 1; + if (!m->dblscan) + wd <<= 1; + vt &= ~1; + } + hw->CRTCEXT[0] |= (wd & 0x300) >> 4; + hw->CRTCEXT[1] = (((ht - 4) & 0x100) >> 8) | + ((hd & 0x100) >> 7) | /* blanking */ + ((hs & 0x100) >> 6) | /* sync start */ + (hbe & 0x040); /* end hor. blanking */ + /* FIXME: Enable vidrst only on G400, and only if TV-out is used */ + if (minfo->outputs[1].src == MATROXFB_SRC_CRTC1) + hw->CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */ + hw->CRTCEXT[2] = ((vt & 0xC00) >> 10) | + ((vd & 0x400) >> 8) | /* disp end */ + ((vd & 0xC00) >> 7) | /* vblanking start */ + ((vs & 0xC00) >> 5) | + ((lc & 0x400) >> 3); + hw->CRTCEXT[3] = (divider - 1) | 0x80; + hw->CRTCEXT[4] = 0; + + hw->CRTC[0] = ht-4; + hw->CRTC[1] = hd; + hw->CRTC[2] = hd; + hw->CRTC[3] = (hbe & 0x1F) | 0x80; + hw->CRTC[4] = hs; + hw->CRTC[5] = ((hbe & 0x20) << 2) | (he & 0x1F); + hw->CRTC[6] = vt & 0xFF; + hw->CRTC[7] = ((vt & 0x100) >> 8) | + ((vd & 0x100) >> 7) | + ((vs & 0x100) >> 6) | + ((vd & 0x100) >> 5) | + ((lc & 0x100) >> 4) | + ((vt & 0x200) >> 4) | + ((vd & 0x200) >> 3) | + ((vs & 0x200) >> 2); + hw->CRTC[8] = 0x00; + hw->CRTC[9] = ((vd & 0x200) >> 4) | + ((lc & 0x200) >> 3); + if (m->dblscan && !m->interlaced) + hw->CRTC[9] |= 0x80; + for (i = 10; i < 16; i++) + hw->CRTC[i] = 0x00; + hw->CRTC[16] = vs /* & 0xFF */; + hw->CRTC[17] = (ve & 0x0F) | 0x20; + hw->CRTC[18] = vd /* & 0xFF */; + hw->CRTC[19] = wd /* & 0xFF */; + hw->CRTC[20] = 0x00; + hw->CRTC[21] = vd /* & 0xFF */; + hw->CRTC[22] = (vt + 1) /* & 0xFF */; + hw->CRTC[23] = 0xC3; + hw->CRTC[24] = lc; + return 0; +}; + +void matroxfb_vgaHWrestore(struct matrox_fb_info *minfo) +{ + int i; + struct matrox_hw_state * const hw = &minfo->hw; + CRITFLAGS + + DBG(__func__) + + dprintk(KERN_INFO "MiscOutReg: %02X\n", hw->MiscOutReg); + dprintk(KERN_INFO "SEQ regs: "); + for (i = 0; i < 5; i++) + dprintk("%02X:", hw->SEQ[i]); + dprintk("\n"); + dprintk(KERN_INFO "GDC regs: "); + for (i = 0; i < 9; i++) + dprintk("%02X:", hw->GCTL[i]); + dprintk("\n"); + dprintk(KERN_INFO "CRTC regs: "); + for (i = 0; i < 25; i++) + dprintk("%02X:", hw->CRTC[i]); + dprintk("\n"); + dprintk(KERN_INFO "ATTR regs: "); + for (i = 0; i < 21; i++) + dprintk("%02X:", hw->ATTR[i]); + dprintk("\n"); + + CRITBEGIN + + mga_inb(M_ATTR_RESET); + mga_outb(M_ATTR_INDEX, 0); + mga_outb(M_MISC_REG, hw->MiscOutReg); + for (i = 1; i < 5; i++) + mga_setr(M_SEQ_INDEX, i, hw->SEQ[i]); + mga_setr(M_CRTC_INDEX, 17, hw->CRTC[17] & 0x7F); + for (i = 0; i < 25; i++) + mga_setr(M_CRTC_INDEX, i, hw->CRTC[i]); + for (i = 0; i < 9; i++) + mga_setr(M_GRAPHICS_INDEX, i, hw->GCTL[i]); + for (i = 0; i < 21; i++) { + mga_inb(M_ATTR_RESET); + mga_outb(M_ATTR_INDEX, i); + mga_outb(M_ATTR_INDEX, hw->ATTR[i]); + } + mga_outb(M_PALETTE_MASK, 0xFF); + mga_outb(M_DAC_REG, 0x00); + for (i = 0; i < 768; i++) + mga_outb(M_DAC_VAL, hw->DACpal[i]); + mga_inb(M_ATTR_RESET); + mga_outb(M_ATTR_INDEX, 0x20); + + CRITEND +} + +static void get_pins(unsigned char __iomem* pins, struct matrox_bios* bd) { + unsigned int b0 = readb(pins); + + if (b0 == 0x2E && readb(pins+1) == 0x41) { + unsigned int pins_len = readb(pins+2); + unsigned int i; + unsigned char cksum; + unsigned char* dst = bd->pins; + + if (pins_len < 3 || pins_len > 128) { + return; + } + *dst++ = 0x2E; + *dst++ = 0x41; + *dst++ = pins_len; + cksum = 0x2E + 0x41 + pins_len; + for (i = 3; i < pins_len; i++) { + cksum += *dst++ = readb(pins+i); + } + if (cksum) { + return; + } + bd->pins_len = pins_len; + } else if (b0 == 0x40 && readb(pins+1) == 0x00) { + unsigned int i; + unsigned char* dst = bd->pins; + + *dst++ = 0x40; + *dst++ = 0; + for (i = 2; i < 0x40; i++) { + *dst++ = readb(pins+i); + } + bd->pins_len = 0x40; + } +} + +static void get_bios_version(unsigned char __iomem * vbios, struct matrox_bios* bd) { + unsigned int pcir_offset; + + pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8); + if (pcir_offset >= 26 && pcir_offset < 0xFFE0 && + readb(vbios + pcir_offset ) == 'P' && + readb(vbios + pcir_offset + 1) == 'C' && + readb(vbios + pcir_offset + 2) == 'I' && + readb(vbios + pcir_offset + 3) == 'R') { + unsigned char h; + + h = readb(vbios + pcir_offset + 0x12); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = readb(vbios + pcir_offset + 0x13); + } else { + unsigned char h; + + h = readb(vbios + 5); + bd->version.vMaj = (h >> 4) & 0xF; + bd->version.vMin = h & 0xF; + bd->version.vRev = 0; + } +} + +static void get_bios_output(unsigned char __iomem* vbios, struct matrox_bios* bd) { + unsigned char b; + + b = readb(vbios + 0x7FF1); + if (b == 0xFF) { + b = 0; + } + bd->output.state = b; +} + +static void get_bios_tvout(unsigned char __iomem* vbios, struct matrox_bios* bd) { + unsigned int i; + + /* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */ + bd->output.tvout = 0; + if (readb(vbios + 0x1D) != 'I' || + readb(vbios + 0x1E) != 'B' || + readb(vbios + 0x1F) != 'M' || + readb(vbios + 0x20) != ' ') { + return; + } + for (i = 0x2D; i < 0x2D + 128; i++) { + unsigned char b = readb(vbios + i); + + if (b == '(' && readb(vbios + i + 1) == 'V') { + if (readb(vbios + i + 6) == 'T' && + readb(vbios + i + 7) == 'V' && + readb(vbios + i + 8) == 'O') { + bd->output.tvout = 1; + } + return; + } + if (b == 0) + break; + } +} + +static void parse_bios(unsigned char __iomem* vbios, struct matrox_bios* bd) { + unsigned int pins_offset; + + if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) { + return; + } + bd->bios_valid = 1; + get_bios_version(vbios, bd); + get_bios_output(vbios, bd); + get_bios_tvout(vbios, bd); +#if defined(__powerpc__) + /* On PowerPC cards, the PInS offset isn't stored at the end of the + * BIOS image. Instead, you must search the entire BIOS image for + * the magic PInS signature. + * + * This actually applies to all OpenFirmware base cards. Since these + * cards could be put in a MIPS or SPARC system, should the condition + * be something different? + */ + for ( pins_offset = 0 ; pins_offset <= 0xFF80 ; pins_offset++ ) { + unsigned char header[3]; + + header[0] = readb(vbios + pins_offset); + header[1] = readb(vbios + pins_offset + 1); + header[2] = readb(vbios + pins_offset + 2); + if ( (header[0] == 0x2E) && (header[1] == 0x41) + && ((header[2] == 0x40) || (header[2] == 0x80)) ) { + printk(KERN_INFO "PInS data found at offset %u\n", + pins_offset); + get_pins(vbios + pins_offset, bd); + break; + } + } +#else + pins_offset = readb(vbios + 0x7FFC) | (readb(vbios + 0x7FFD) << 8); + if (pins_offset <= 0xFF80) { + get_pins(vbios + pins_offset, bd); + } +#endif +} + +static int parse_pins1(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + unsigned int maxdac; + + switch (bd->pins[22]) { + case 0: maxdac = 175000; break; + case 1: maxdac = 220000; break; + default: maxdac = 240000; break; + } + if (get_unaligned_le16(bd->pins + 24)) { + maxdac = get_unaligned_le16(bd->pins + 24) * 10; + } + minfo->limits.pixel.vcomax = maxdac; + minfo->values.pll.system = get_unaligned_le16(bd->pins + 28) ? + get_unaligned_le16(bd->pins + 28) * 10 : 50000; + /* ignore 4MB, 8MB, module clocks */ + minfo->features.pll.ref_freq = 14318; + minfo->values.reg.mctlwtst = 0x00030101; + return 0; +} + +static void default_pins1(struct matrox_fb_info *minfo) +{ + /* Millennium */ + minfo->limits.pixel.vcomax = 220000; + minfo->values.pll.system = 50000; + minfo->features.pll.ref_freq = 14318; + minfo->values.reg.mctlwtst = 0x00030101; +} + +static int parse_pins2(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000); + minfo->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) | + ((bd->pins[51] & 0x02) ? 0x00000100 : 0) | + ((bd->pins[51] & 0x04) ? 0x00010000 : 0) | + ((bd->pins[51] & 0x08) ? 0x00020000 : 0); + minfo->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000); + minfo->features.pll.ref_freq = 14318; + return 0; +} + +static void default_pins2(struct matrox_fb_info *minfo) +{ + /* Millennium II, Mystique */ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 230000; + minfo->values.reg.mctlwtst = 0x00030101; + minfo->values.pll.system = 50000; + minfo->features.pll.ref_freq = 14318; +} + +static int parse_pins3(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000); + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 48) == 0xFFFFFFFF ? + 0x01250A21 : get_unaligned_le32(bd->pins + 48); + /* memory config */ + minfo->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) | + ((bd->pins[57] << 22) & 0x00C00000) | + ((bd->pins[56] << 1) & 0x000001E0) | + ( bd->pins[56] & 0x0000000F); + minfo->values.reg.opt = (bd->pins[54] & 7) << 10; + minfo->values.reg.opt2 = bd->pins[58] << 12; + minfo->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000; + return 0; +} + +static void default_pins3(struct matrox_fb_info *minfo) +{ + /* G100, G200 */ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 230000; + minfo->values.reg.mctlwtst = 0x01250A21; + minfo->values.reg.memrdbk = 0x00000000; + minfo->values.reg.opt = 0x00000C00; + minfo->values.reg.opt2 = 0x00000000; + minfo->features.pll.ref_freq = 27000; +} + +static int parse_pins4(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + minfo->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000; + minfo->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? minfo->limits.pixel.vcomax : bd->pins[ 38] * 4000; + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 71); + minfo->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) | + ((bd->pins[87] << 22) & 0x00C00000) | + ((bd->pins[86] << 1) & 0x000001E0) | + ( bd->pins[86] & 0x0000000F); + minfo->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) | + ((bd->pins[53] << 22) & 0x10000000) | + ((bd->pins[53] << 7) & 0x00001C00); + minfo->values.reg.opt3 = get_unaligned_le32(bd->pins + 67); + minfo->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000; + minfo->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000; + return 0; +} + +static void default_pins4(struct matrox_fb_info *minfo) +{ + /* G400 */ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = 252000; + minfo->values.reg.mctlwtst = 0x04A450A1; + minfo->values.reg.memrdbk = 0x000000E7; + minfo->values.reg.opt = 0x10000400; + minfo->values.reg.opt3 = 0x0190A419; + minfo->values.pll.system = 200000; + minfo->features.pll.ref_freq = 27000; +} + +static int parse_pins5(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + unsigned int mult; + + mult = bd->pins[4]?8000:6000; + + minfo->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult; + minfo->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? minfo->limits.pixel.vcomax : bd->pins[ 36] * mult; + minfo->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? minfo->limits.system.vcomax : bd->pins[ 37] * mult; + minfo->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult; + minfo->limits.system.vcomin = (bd->pins[121] == 0xFF) ? minfo->limits.pixel.vcomin : bd->pins[121] * mult; + minfo->limits.video.vcomin = (bd->pins[122] == 0xFF) ? minfo->limits.system.vcomin : bd->pins[122] * mult; + minfo->values.pll.system = + minfo->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000; + minfo->values.reg.opt = get_unaligned_le32(bd->pins + 48); + minfo->values.reg.opt2 = get_unaligned_le32(bd->pins + 52); + minfo->values.reg.opt3 = get_unaligned_le32(bd->pins + 94); + minfo->values.reg.mctlwtst = get_unaligned_le32(bd->pins + 98); + minfo->values.reg.memmisc = get_unaligned_le32(bd->pins + 102); + minfo->values.reg.memrdbk = get_unaligned_le32(bd->pins + 106); + minfo->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000; + minfo->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20; + minfo->values.memory.dll = (bd->pins[115] & 0x02) != 0; + minfo->values.memory.emrswen = (bd->pins[115] & 0x01) != 0; + minfo->values.reg.maccess = minfo->values.memory.emrswen ? 0x00004000 : 0x00000000; + if (bd->pins[115] & 4) { + minfo->values.reg.mctlwtst_core = minfo->values.reg.mctlwtst; + } else { + u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 }; + minfo->values.reg.mctlwtst_core = (minfo->values.reg.mctlwtst & ~7) | + wtst_xlat[minfo->values.reg.mctlwtst & 7]; + } + minfo->max_pixel_clock_panellink = bd->pins[47] * 4000; + return 0; +} + +static void default_pins5(struct matrox_fb_info *minfo) +{ + /* Mine 16MB G450 with SDRAM DDR */ + minfo->limits.pixel.vcomax = + minfo->limits.system.vcomax = + minfo->limits.video.vcomax = 600000; + minfo->limits.pixel.vcomin = + minfo->limits.system.vcomin = + minfo->limits.video.vcomin = 256000; + minfo->values.pll.system = + minfo->values.pll.video = 284000; + minfo->values.reg.opt = 0x404A1160; + minfo->values.reg.opt2 = 0x0000AC00; + minfo->values.reg.opt3 = 0x0090A409; + minfo->values.reg.mctlwtst_core = + minfo->values.reg.mctlwtst = 0x0C81462B; + minfo->values.reg.memmisc = 0x80000004; + minfo->values.reg.memrdbk = 0x01001103; + minfo->features.pll.ref_freq = 27000; + minfo->values.memory.ddr = 1; + minfo->values.memory.dll = 1; + minfo->values.memory.emrswen = 1; + minfo->values.reg.maccess = 0x00004000; +} + +static int matroxfb_set_limits(struct matrox_fb_info *minfo, + const struct matrox_bios *bd) +{ + unsigned int pins_version; + static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 }; + + switch (minfo->chip) { + case MGA_2064: default_pins1(minfo); break; + case MGA_2164: + case MGA_1064: + case MGA_1164: default_pins2(minfo); break; + case MGA_G100: + case MGA_G200: default_pins3(minfo); break; + case MGA_G400: default_pins4(minfo); break; + case MGA_G450: + case MGA_G550: default_pins5(minfo); break; + } + if (!bd->bios_valid) { + printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n"); + return -1; + } + if (bd->pins_len < 64) { + printk(KERN_INFO "matroxfb: BIOS on your Matrox device does not contain powerup info\n"); + return -1; + } + if (bd->pins[0] == 0x2E && bd->pins[1] == 0x41) { + pins_version = bd->pins[5]; + if (pins_version < 2 || pins_version > 5) { + printk(KERN_INFO "matroxfb: Unknown version (%u) of powerup info\n", pins_version); + return -1; + } + } else { + pins_version = 1; + } + if (bd->pins_len != pinslen[pins_version - 1]) { + printk(KERN_INFO "matroxfb: Invalid powerup info\n"); + return -1; + } + switch (pins_version) { + case 1: + return parse_pins1(minfo, bd); + case 2: + return parse_pins2(minfo, bd); + case 3: + return parse_pins3(minfo, bd); + case 4: + return parse_pins4(minfo, bd); + case 5: + return parse_pins5(minfo, bd); + default: + printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version); + return -1; + } +} + +void matroxfb_read_pins(struct matrox_fb_info *minfo) +{ + u32 opt; + u32 biosbase; + u32 fbbase; + struct pci_dev *pdev = minfo->pcidev; + + memset(&minfo->bios, 0, sizeof(minfo->bios)); + pci_read_config_dword(pdev, PCI_OPTION_REG, &opt); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM); + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase); + pci_read_config_dword(pdev, minfo->devflags.fbResource, &fbbase); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE); + parse_bios(vaddr_va(minfo->video.vbase), &minfo->bios); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase); + pci_write_config_dword(pdev, PCI_OPTION_REG, opt); +#ifdef CONFIG_X86 + if (!minfo->bios.bios_valid) { + unsigned char __iomem* b; + + b = ioremap(0x000C0000, 65536); + if (!b) { + printk(KERN_INFO "matroxfb: Unable to map legacy BIOS\n"); + } else { + unsigned int ven = readb(b+0x64+0) | (readb(b+0x64+1) << 8); + unsigned int dev = readb(b+0x64+2) | (readb(b+0x64+3) << 8); + + if (ven != pdev->vendor || dev != pdev->device) { + printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n", + ven, dev, pdev->vendor, pdev->device); + } else { + parse_bios(b, &minfo->bios); + } + iounmap(b); + } + } +#endif + matroxfb_set_limits(minfo, &minfo->bios); + printk(KERN_INFO "PInS memtype = %u\n", + (minfo->values.reg.opt & 0x1C00) >> 10); +} + +EXPORT_SYMBOL(matroxfb_DAC_in); +EXPORT_SYMBOL(matroxfb_DAC_out); +EXPORT_SYMBOL(matroxfb_var2my); +EXPORT_SYMBOL(matroxfb_PLL_calcclock); +EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */ +EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */ +EXPORT_SYMBOL(matroxfb_read_pins); + +MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec vandrove@vc.cvut.cz"); +MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_misc.h b/drivers/staging/mgakms/matroxfb_misc.h new file mode 100644 index 000000000000..9cb6686d309e --- /dev/null +++ b/drivers/staging/mgakms/matroxfb_misc.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MATROXFB_MISC_H__ +#define __MATROXFB_MISC_H__ + +#include "matroxfb_base.h" + +/* also for modules */ +int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax, + unsigned int* in, unsigned int* feed, unsigned int* post); +static inline int PLL_calcclock(const struct matrox_fb_info *minfo, + unsigned int freq, unsigned int fmax, + unsigned int *in, unsigned int *feed, + unsigned int *post) +{ + return matroxfb_PLL_calcclock(&minfo->features.pll, freq, fmax, in, feed, post); +} + +int matroxfb_vgaHWinit(struct matrox_fb_info *minfo, struct my_timming* m); +void matroxfb_vgaHWrestore(struct matrox_fb_info *minfo); +void matroxfb_read_pins(struct matrox_fb_info *minfo); + +#endif /* __MATROXFB_MISC_H__ */
On Mon, Oct 14, 2019 at 04:04:15PM +0200, Thomas Zimmermann wrote:
Only code is being copied, no functional changes are made.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
drivers/staging/mgakms/g450_pll.c | 539 +++++a
...
Personally I would start from scratch. In fact some years (*cough* decade) ago I did just that and started writing a new driver for matrox stuff. Unfortunately I ran out of steam after figuring out most of the interesting hardware quirks and whatnot, so I never finished it. The end result is that it still runs in userspace but kinda looks like a kernel driver if you squint a bit.
Anyways, I just slapped a MIT license on it dumped the whole thing here: https://gitlab.com/syrjala/mga The development history was, shall we say, not really useful so I just squashed it.
You, or someone else, might find it interesting. I think in terms of hardware support it's a superset of any other driver, apart from the blob.
Hi
Am 15.10.19 um 13:48 schrieb Ville Syrjälä:
On Mon, Oct 14, 2019 at 04:04:15PM +0200, Thomas Zimmermann wrote:
Only code is being copied, no functional changes are made.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de
drivers/staging/mgakms/g450_pll.c | 539 +++++a
...
Personally I would start from scratch. In fact some years (*cough* decade) ago I did just that and started writing a new driver for matrox stuff. Unfortunately I ran out of steam after figuring out most of the interesting hardware quirks and whatnot, so I never finished it. The end result is that it still runs in userspace but kinda looks like a kernel driver if you squint a bit.
Anyways, I just slapped a MIT license on it dumped the whole thing here: https://gitlab.com/syrjala/mga The development history was, shall we say, not really useful so I just squashed it.
You, or someone else, might find it interesting. I think in terms of hardware support it's a superset of any other driver, apart from the blob.
Just to make this clear: I do not intend to port every single fbdev driver to DRM. :)
I did, however, began to convert that Matrox driver. First, to see if the approach does work in general; and because matroxfb is one of the more complex drivers. If it can be converted, any other driver should be convertible as well. I split up the driver code by HW generation and can now refactor each generation on its own. I expect to end up with several duplicated functions, which can be re-merged.
Maybe our repo can be helpful. Thanks for the link.
Best regards Thomas
With the update, all driver code coming from matroxfb is build unconditionally. The driver registers itself with the mgakms driver, instead of the fbdev core.
Signed-off-by: Thomas Zimmermann tzimmermann@suse.de --- drivers/staging/mgakms/Kconfig | 5 ++ drivers/staging/mgakms/Makefile | 11 ++++ drivers/staging/mgakms/matroxfb_DAC1064.c | 77 +++++++---------------- drivers/staging/mgakms/matroxfb_DAC1064.h | 6 -- drivers/staging/mgakms/matroxfb_Ti3026.c | 2 - drivers/staging/mgakms/matroxfb_Ti3026.h | 2 - drivers/staging/mgakms/matroxfb_base.c | 29 +++------ drivers/staging/mgakms/matroxfb_base.h | 20 ++---- drivers/staging/mgakms/matroxfb_g450.h | 5 -- 9 files changed, 51 insertions(+), 106 deletions(-)
diff --git a/drivers/staging/mgakms/Kconfig b/drivers/staging/mgakms/Kconfig index de23e76317bd..66c5b3b8669f 100644 --- a/drivers/staging/mgakms/Kconfig +++ b/drivers/staging/mgakms/Kconfig @@ -4,6 +4,11 @@ config DRM_MGAKMS select DRM_FBCONV_HELPER select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER + select FB_CFB_COPYAREA + select FB_CFB_FILLRECT + select FB_CFB_IMAGEBLIT + select FB_MACMODES if PPC_PMAC + select FB_TILEBLITTING help Choose this option if you have a Matrox Millennium, Matrox Millennium II, Matrox Mystique, Matrox Mystique 220, diff --git a/drivers/staging/mgakms/Makefile b/drivers/staging/mgakms/Makefile index 65695f04eb7f..b368f8a5e38a 100644 --- a/drivers/staging/mgakms/Makefile +++ b/drivers/staging/mgakms/Makefile @@ -3,4 +3,15 @@ mgakms-y := mga_device.o \ mga_drv.o
+# Old matroxfb driver code. Keep this separate and +# remove it after conversion. +mgakms-y += g450_pll.o \ + i2c-matroxfb.o \ + matroxfb_accel.o \ + matroxfb_base.o \ + matroxfb_DAC1064.o \ + matroxfb_g450.o \ + matroxfb_misc.o \ + matroxfb_Ti3026.o + obj-$(CONFIG_DRM_MGAKMS) += mgakms.o diff --git a/drivers/staging/mgakms/matroxfb_DAC1064.c b/drivers/staging/mgakms/matroxfb_DAC1064.c index b380a393cbc3..6bb214c548af 100644 --- a/drivers/staging/mgakms/matroxfb_DAC1064.c +++ b/drivers/staging/mgakms/matroxfb_DAC1064.c @@ -19,7 +19,6 @@ #include "g450_pll.h" #include <linux/matroxfb.h>
-#ifdef NEED_DAC1064 #define outDAC1064 matroxfb_DAC_out #define inDAC1064 matroxfb_DAC_in
@@ -42,11 +41,11 @@ static void DAC1064_calcclock(const struct matrox_fb_info *minfo, unsigned int p;
DBG(__func__) - + /* only for devices older than G450 */
fvco = PLL_calcclock(minfo, freq, fmax, in, feed, &p); - + p = (1 << p) - 1; if (fvco <= 100000) ; @@ -160,7 +159,6 @@ static void DAC1064_setmclk(struct matrox_fb_info *minfo, int oscinfo, hw->MXoptionReg = mx; }
-#ifdef CONFIG_FB_MATROX_G static void g450_set_plls(struct matrox_fb_info *minfo) { u_int32_t c2_ctl; @@ -168,7 +166,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) struct matrox_hw_state *hw = &minfo->hw; int pixelmnp; int videomnp; - + c2_ctl = hw->crtc2.ctl & ~0x4007; /* Clear PLL + enable for CRTC2 */ c2_ctl |= 0x0001; /* Enable CRTC2 */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x02; /* Stop VIDEO PLL */ @@ -191,7 +189,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) } c2_ctl |= 0x0006; /* Use video PLL */ hw->DACreg[POS1064_XPWRCTRL] |= 0x02; - + outDAC1064(minfo, M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]); matroxfb_g450_setpll_cond(minfo, videomnp, M_VIDEO_PLL); } @@ -199,7 +197,7 @@ static void g450_set_plls(struct matrox_fb_info *minfo) hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP; if (pixelmnp >= 0) { hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP; - + outDAC1064(minfo, M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]); matroxfb_g450_setpll_cond(minfo, pixelmnp, M_PIXEL_PLL_C); } @@ -251,7 +249,6 @@ static void g450_set_plls(struct matrox_fb_info *minfo) } } } -#endif
void DAC1064_global_init(struct matrox_fb_info *minfo) { @@ -260,7 +257,6 @@ void DAC1064_global_init(struct matrox_fb_info *minfo) hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK; hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN; hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL; -#ifdef CONFIG_FB_MATROX_G if (minfo->devflags.g450dac) { hw->DACreg[POS1064_XPWRCTRL] = 0x1F; /* powerup everything */ hw->DACreg[POS1064_XOUTPUTCONN] = 0x00; /* disable outputs */ @@ -302,15 +298,14 @@ void DAC1064_global_init(struct matrox_fb_info *minfo) poweroff TMDS. But if we boot with DFP connected, TMDS generated clocks are used instead of ALL pixclocks available... If someone knows which register - handles it, please reveal this secret to me... */ + handles it, please reveal this secret to me... */ hw->DACreg[POS1064_XPWRCTRL] &= ~0x04; /* Poweroff TMDS */ -#endif +#endif break; } /* Now set timming related variables... */ g450_set_plls(minfo); } else -#endif { if (minfo->outputs[1].src == MATROXFB_SRC_CRTC1) { hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; @@ -510,7 +505,6 @@ static struct matrox_altout m1064 = { .compute = m1064_compute, };
-#ifdef CONFIG_FB_MATROX_G static int g450_compute(void* out, struct my_timming* m) { #define minfo ((struct matrox_fb_info*)out) if (m->mnp < 0) { @@ -527,11 +521,7 @@ static struct matrox_altout g450out = { .name = "Primary output", .compute = g450_compute, }; -#endif
-#endif /* NEED_DAC1064 */ - -#ifdef CONFIG_FB_MATROX_MYSTIQUE static int MGA1064_init(struct matrox_fb_info *minfo, struct my_timming *m) { struct matrox_hw_state *hw = &minfo->hw; @@ -552,9 +542,7 @@ static int MGA1064_init(struct matrox_fb_info *minfo, struct my_timming *m) if (DAC1064_init_2(minfo, m)) return 1; return 0; } -#endif
-#ifdef CONFIG_FB_MATROX_G static int MGAG100_init(struct matrox_fb_info *minfo, struct my_timming *m) { struct matrox_hw_state *hw = &minfo->hw; @@ -576,9 +564,7 @@ static int MGAG100_init(struct matrox_fb_info *minfo, struct my_timming *m) if (DAC1064_init_2(minfo, m)) return 1; return 0; } -#endif /* G */
-#ifdef CONFIG_FB_MATROX_MYSTIQUE static void MGA1064_ramdac_init(struct matrox_fb_info *minfo) {
@@ -596,9 +582,7 @@ static void MGA1064_ramdac_init(struct matrox_fb_info *minfo) /* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */ DAC1064_setmclk(minfo, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333); } -#endif
-#ifdef CONFIG_FB_MATROX_G /* BIOS environ */ static int x7AF4 = 0x10; /* flags, maybe 0x10 = SDRAM, 0x00 = SGRAM??? */ /* G100 wants 0x10, G200 SGRAM does not care... */ @@ -662,9 +646,7 @@ static void MGAG100_setPixClock(const struct matrox_fb_info *minfo, int flags, DAC1064_calcclock(minfo, freq, minfo->max_pixel_clock, &m, &n, &p); MGAG100_progPixClock(minfo, flags, m, n, p); } -#endif
-#ifdef CONFIG_FB_MATROX_MYSTIQUE static int MGA1064_preinit(struct matrox_fb_info *minfo) { static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960, @@ -710,9 +692,7 @@ static void MGA1064_reset(struct matrox_fb_info *minfo)
MGA1064_ramdac_init(minfo); } -#endif
-#ifdef CONFIG_FB_MATROX_G static void g450_mclk_init(struct matrox_fb_info *minfo) { /* switch all clocks to PCI source */ @@ -727,14 +707,14 @@ static void g450_mclk_init(struct matrox_fb_info *minfo) } else { unsigned long flags; unsigned int pwr; - + matroxfb_DAC_lock_irqsave(flags); pwr = inDAC1064(minfo, M1064_XPWRCTRL) & ~0x02; outDAC1064(minfo, M1064_XPWRCTRL, pwr); matroxfb_DAC_unlock_irqrestore(flags); } matroxfb_g450_setclk(minfo, minfo->values.pll.system, M_SYSTEM_PLL); - + /* switch clocks to their real PLL source(s) */ pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg | 4); pci_write_config_dword(minfo->pcidev, PCI_OPTION3_REG, minfo->values.reg.opt3); @@ -747,15 +727,15 @@ static void g450_memory_init(struct matrox_fb_info *minfo) /* disable memory refresh */ minfo->hw.MXoptionReg &= ~0x001F8000; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); - + /* set memory interface parameters */ minfo->hw.MXoptionReg &= ~0x00207E00; minfo->hw.MXoptionReg |= 0x00207E00 & minfo->values.reg.opt; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); pci_write_config_dword(minfo->pcidev, PCI_OPTION2_REG, minfo->values.reg.opt2); - + mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst); - + /* first set up memory interface with disabled memory interface clocks */ pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc & ~0x80000000U); mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk); @@ -764,25 +744,25 @@ static void g450_memory_init(struct matrox_fb_info *minfo) pci_write_config_dword(minfo->pcidev, PCI_MEMMISC_REG, minfo->values.reg.memmisc | 0x80000000U);
udelay(200); - + if (minfo->values.memory.ddr && (!minfo->values.memory.emrswen || !minfo->values.memory.dll)) { mga_outl(M_MEMRDBK, minfo->values.reg.memrdbk & ~0x1000); } mga_outl(M_MACCESS, minfo->values.reg.maccess | 0x8000); - + udelay(200); - + minfo->hw.MXoptionReg |= 0x001F8000 & minfo->values.reg.opt; pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg); - + /* value is written to memory chips only if old != new */ mga_outl(M_PLNWT, 0); mga_outl(M_PLNWT, ~0); - + if (minfo->values.reg.mctlwtst != minfo->values.reg.mctlwtst_core) { mga_outl(M_CTLWTST, minfo->values.reg.mctlwtst_core); } - + }
static void g450_preinit(struct matrox_fb_info *minfo) @@ -790,7 +770,7 @@ static void g450_preinit(struct matrox_fb_info *minfo) u_int32_t c2ctl; u_int8_t curctl; u_int8_t c1ctl; - + /* minfo->hw.MXoptionReg = minfo->values.reg.opt; */ minfo->hw.MXoptionReg &= 0xC0000100; minfo->hw.MXoptionReg |= 0x00000020; @@ -804,7 +784,7 @@ static void g450_preinit(struct matrox_fb_info *minfo) pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, minfo->hw.MXoptionReg);
/* Init system clocks */ - + /* stop crtc2 */ c2ctl = mga_inl(M_C2CTL); mga_outl(M_C2CTL, c2ctl & ~1); @@ -817,20 +797,20 @@ static void g450_preinit(struct matrox_fb_info *minfo)
g450_mclk_init(minfo); g450_memory_init(minfo); - + /* set legacy VGA clock sources for DOSEmu or VMware... */ matroxfb_g450_setclk(minfo, 25175, M_PIXEL_PLL_A); matroxfb_g450_setclk(minfo, 28322, M_PIXEL_PLL_B);
/* restore crtc1 */ mga_setr(M_SEQ_INDEX, 1, c1ctl); - + /* restore cursor */ outDAC1064(minfo, M1064_XCURCTRL, curctl);
/* restore crtc2 */ mga_outl(M_C2CTL, c2ctl); - + return; }
@@ -1031,9 +1011,7 @@ static void MGAG100_reset(struct matrox_fb_info *minfo) } } } -#endif
-#ifdef CONFIG_FB_MATROX_MYSTIQUE static void MGA1064_restore(struct matrox_fb_info *minfo) { int i; @@ -1058,9 +1036,7 @@ static void MGA1064_restore(struct matrox_fb_info *minfo) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); DAC1064_restore_2(minfo); } -#endif
-#ifdef CONFIG_FB_MATROX_G static void MGAG100_restore(struct matrox_fb_info *minfo) { int i; @@ -1084,9 +1060,7 @@ static void MGAG100_restore(struct matrox_fb_info *minfo) mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]); DAC1064_restore_2(minfo); } -#endif
-#ifdef CONFIG_FB_MATROX_MYSTIQUE struct matrox_switch matrox_mystique = { .preinit = MGA1064_preinit, .reset = MGA1064_reset, @@ -1094,9 +1068,7 @@ struct matrox_switch matrox_mystique = { .restore = MGA1064_restore, }; EXPORT_SYMBOL(matrox_mystique); -#endif
-#ifdef CONFIG_FB_MATROX_G struct matrox_switch matrox_G100 = { .preinit = MGAG100_preinit, .reset = MGAG100_reset, @@ -1104,10 +1076,7 @@ struct matrox_switch matrox_G100 = { .restore = MGAG100_restore, }; EXPORT_SYMBOL(matrox_G100); -#endif
-#ifdef NEED_DAC1064 EXPORT_SYMBOL(DAC1064_global_init); EXPORT_SYMBOL(DAC1064_global_restore); -#endif MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_DAC1064.h b/drivers/staging/mgakms/matroxfb_DAC1064.h index 3b2a6fd35fff..3d388941b51d 100644 --- a/drivers/staging/mgakms/matroxfb_DAC1064.h +++ b/drivers/staging/mgakms/matroxfb_DAC1064.h @@ -5,16 +5,10 @@
#include "matroxfb_base.h"
-#ifdef CONFIG_FB_MATROX_MYSTIQUE extern struct matrox_switch matrox_mystique; -#endif -#ifdef CONFIG_FB_MATROX_G extern struct matrox_switch matrox_G100; -#endif -#ifdef NEED_DAC1064 void DAC1064_global_init(struct matrox_fb_info *minfo); void DAC1064_global_restore(struct matrox_fb_info *minfo); -#endif
#define M1064_INDEX 0x00 #define M1064_PALWRADD 0x00 diff --git a/drivers/staging/mgakms/matroxfb_Ti3026.c b/drivers/staging/mgakms/matroxfb_Ti3026.c index 9ff9be85759e..e82e2a3cf76a 100644 --- a/drivers/staging/mgakms/matroxfb_Ti3026.c +++ b/drivers/staging/mgakms/matroxfb_Ti3026.c @@ -84,7 +84,6 @@ #include "matroxfb_accel.h" #include <linux/matroxfb.h>
-#ifdef CONFIG_FB_MATROX_MILLENIUM #define outTi3026 matroxfb_DAC_out #define inTi3026 matroxfb_DAC_in
@@ -744,5 +743,4 @@ struct matrox_switch matrox_millennium = { .restore = Ti3026_restore }; EXPORT_SYMBOL(matrox_millennium); -#endif MODULE_LICENSE("GPL"); diff --git a/drivers/staging/mgakms/matroxfb_Ti3026.h b/drivers/staging/mgakms/matroxfb_Ti3026.h index faee149d0ba0..2fda3b30b9e7 100644 --- a/drivers/staging/mgakms/matroxfb_Ti3026.h +++ b/drivers/staging/mgakms/matroxfb_Ti3026.h @@ -5,8 +5,6 @@
#include "matroxfb_base.h"
-#ifdef CONFIG_FB_MATROX_MILLENIUM extern struct matrox_switch matrox_millennium; -#endif
#endif /* __MATROXFB_TI3026_H__ */ diff --git a/drivers/staging/mgakms/matroxfb_base.c b/drivers/staging/mgakms/matroxfb_base.c index d11b5e6210ed..7ee0ea046a5f 100644 --- a/drivers/staging/mgakms/matroxfb_base.c +++ b/drivers/staging/mgakms/matroxfb_base.c @@ -114,6 +114,7 @@ #include <linux/nvram.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include "mga_drv.h"
#ifdef CONFIG_PPC_PMAC #include <asm/machdep.h> @@ -368,7 +369,7 @@ static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy) return; } matroxfb_unregister_device(minfo); - unregister_framebuffer(&minfo->fbcon); + mga_unregister_framebuffer(minfo->mdev); matroxfb_g450_shutdown(minfo); arch_phys_wc_del(minfo->wc_cookie); iounmap(minfo->mmio.vbase.vaddr); @@ -1319,9 +1320,7 @@ static int matroxfb_getmemory(struct matrox_fb_info *minfo, mga_outb(M_EXTVGA_DATA, orig);
*realSize = offs - 0x100000; -#ifdef CONFIG_FB_MATROX_MILLENIUM minfo->interleave = !(!isMillenium(minfo) || ((offs - 0x100000) & 0x3FFFFF)); -#endif return 1; }
@@ -1331,7 +1330,6 @@ struct video_board { int accelID; struct matrox_switch* lowlevel; }; -#ifdef CONFIG_FB_MATROX_MILLENIUM static struct video_board vbMillennium = { .maxvram = 0x0800000, .maxdisplayable = 0x0800000, @@ -1352,16 +1350,12 @@ static struct video_board vbMillennium2A = { .accelID = FB_ACCEL_MATROX_MGA2164W_AGP, .lowlevel = &matrox_millennium }; -#endif /* CONFIG_FB_MATROX_MILLENIUM */ -#ifdef CONFIG_FB_MATROX_MYSTIQUE static struct video_board vbMystique = { .maxvram = 0x0800000, .maxdisplayable = 0x0800000, .accelID = FB_ACCEL_MATROX_MGA1064SG, .lowlevel = &matrox_mystique }; -#endif /* CONFIG_FB_MATROX_MYSTIQUE */ -#ifdef CONFIG_FB_MATROX_G static struct video_board vbG100 = { .maxvram = 0x0800000, .maxdisplayable = 0x0800000, @@ -1383,7 +1377,6 @@ static struct video_board vbG400 = { .accelID = FB_ACCEL_MATROX_MGAG400, .lowlevel = &matrox_G100 }; -#endif
#define DEVF_VIDEO64BIT 0x0001 #define DEVF_SWAPS 0x0002 @@ -1418,7 +1411,6 @@ static struct board { struct video_board* base; const char* name; } dev_list[] = { -#ifdef CONFIG_FB_MATROX_MILLENIUM {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, 0xFF, 0, 0, DEVF_TEXT4B, @@ -1440,8 +1432,6 @@ static struct board { MGA_2164, &vbMillennium2A, "Millennium II (AGP)"}, -#endif -#ifdef CONFIG_FB_MATROX_MYSTIQUE {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0x02, 0, 0, DEVF_VIDEO64BIT | DEVF_CROSS4MB, @@ -1470,8 +1460,6 @@ static struct board { MGA_1164, &vbMystique, "Mystique 220 (AGP)"}, -#endif -#ifdef CONFIG_FB_MATROX_G {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF, 0, 0, DEVF_G100, @@ -1563,7 +1551,6 @@ static struct board { MGA_G550, &vbG400, "G550"}, -#endif {0, 0, 0xFF, 0, 0, 0, @@ -1622,6 +1609,7 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) unsigned long video_base_phys = 0; unsigned int memsize; int err; + struct mga_device *mdev;
static const struct pci_device_id intel_82437[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437) }, @@ -1918,9 +1906,12 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b) * and we do not want currcon == 0 for subsequent framebuffers */
minfo->fbcon.device = &minfo->pcidev->dev; - if (register_framebuffer(&minfo->fbcon) < 0) { + mdev = mga_register_framebuffer(&minfo->fbcon, minfo->pcidev); + if (IS_ERR(mdev)) { + err = PTR_ERR(mdev); goto failVideoIO; } + minfo->mdev = mdev; fb_info(&minfo->fbcon, "%s frame buffer device\n", minfo->fbcon.fix.id);
/* there is no console on this fb... but we have to initialize hardware @@ -2116,19 +2107,14 @@ static void pci_remove_matrox(struct pci_dev* pdev) { }
static const struct pci_device_id matroxfb_devices[] = { -#ifdef CONFIG_FB_MATROX_MILLENIUM {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, -#endif -#ifdef CONFIG_FB_MATROX_MYSTIQUE {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, -#endif -#ifdef CONFIG_FB_MATROX_G {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, @@ -2141,7 +2127,6 @@ static const struct pci_device_id matroxfb_devices[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, -#endif {0, 0, 0, 0, 0, 0, 0} }; diff --git a/drivers/staging/mgakms/matroxfb_base.h b/drivers/staging/mgakms/matroxfb_base.h index f85ad25659e5..f7548549b3a6 100644 --- a/drivers/staging/mgakms/matroxfb_base.h +++ b/drivers/staging/mgakms/matroxfb_base.h @@ -115,11 +115,7 @@
#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
-/* G-series and Mystique have (almost) same DAC */ -#undef NEED_DAC1064 -#if defined(CONFIG_FB_MATROX_MYSTIQUE) || defined(CONFIG_FB_MATROX_G) -#define NEED_DAC1064 1 -#endif +struct mga_device;
typedef struct { void __iomem* vaddr; @@ -282,9 +278,7 @@ struct matrox_hw_state { };
struct matrox_accel_data { -#ifdef CONFIG_FB_MATROX_MILLENIUM unsigned char ramdac_rev; -#endif u_int32_t m_dwg_rect; u_int32_t m_opmode; u_int32_t m_access; @@ -302,9 +296,9 @@ struct matrox_altout { int (*verifymode)(void* altout_dev, u_int32_t mode); int (*getqueryctrl)(void* altout_dev, struct v4l2_queryctrl* ctrl); - int (*getctrl)(void* altout_dev, + int (*getctrl)(void* altout_dev, struct v4l2_control* ctrl); - int (*setctrl)(void* altout_dev, + int (*setctrl)(void* altout_dev, struct v4l2_control* ctrl); };
@@ -338,6 +332,8 @@ struct matrox_vsync { struct matrox_fb_info { struct fb_info fbcon;
+ struct mga_device *mdev; + struct list_head next_fb;
int dead; @@ -676,15 +672,9 @@ void matroxfb_unregister_driver(struct matroxfb_driver* drv); #define WaitTillIdle() do { mga_inl(M_STATUS); do {} while (mga_inl(M_STATUS) & 0x10000); } while (0)
/* code speedup */ -#ifdef CONFIG_FB_MATROX_MILLENIUM #define isInterleave(x) (x->interleave) #define isMillenium(x) (x->millenium) #define isMilleniumII(x) (x->milleniumII) -#else -#define isInterleave(x) (0) -#define isMillenium(x) (0) -#define isMilleniumII(x) (0) -#endif
#define matroxfb_DAC_lock() spin_lock(&minfo->lock.DAC) #define matroxfb_DAC_unlock() spin_unlock(&minfo->lock.DAC) diff --git a/drivers/staging/mgakms/matroxfb_g450.h b/drivers/staging/mgakms/matroxfb_g450.h index b5f17b86eae5..2cec778a2a81 100644 --- a/drivers/staging/mgakms/matroxfb_g450.h +++ b/drivers/staging/mgakms/matroxfb_g450.h @@ -4,12 +4,7 @@
#include "matroxfb_base.h"
-#ifdef CONFIG_FB_MATROX_G void matroxfb_g450_connect(struct matrox_fb_info *minfo); void matroxfb_g450_shutdown(struct matrox_fb_info *minfo); -#else -static inline void matroxfb_g450_connect(struct matrox_fb_info *minfo) { }; -static inline void matroxfb_g450_shutdown(struct matrox_fb_info *minfo) { }; -#endif
#endif /* __MATROXFB_G450_H__ */
Hi Thomas,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master] [cannot apply to v5.4-rc3 next-20191017] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Thomas-Zimmermann/DRM-fbconv-helper... config: x86_64-allyesconfig (attached as .config) compiler: gcc-7 (Debian 7.4.0-13) 7.4.0 reproduce: # save the attached .config to linux build tree make ARCH=x86_64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
ld: drivers/staging/mgakms/g450_pll.o: in function `__crc_g450_mnp2f': (*ABS*+0x456b8a98): multiple definition of `__crc_g450_mnp2f' ld: drivers/staging/mgakms/g450_pll.o: in function `__crc_matroxfb_g450_setclk': (*ABS*+0x4da4fa96): multiple definition of `__crc_matroxfb_g450_setclk' ld: drivers/staging/mgakms/g450_pll.o: in function `matroxfb_g450_setclk':
(.text+0x5b0): multiple definition of `matroxfb_g450_setclk'; drivers/video/fbdev/matrox/g450_pll.o:(.text+0x5b0): first defined here
ld: drivers/staging/mgakms/g450_pll.o: in function `g450_mnp2f':
(.text+0x0): multiple definition of `g450_mnp2f'; drivers/video/fbdev/matrox/g450_pll.o:(.text+0x0): first defined here
ld: drivers/staging/mgakms/g450_pll.o: in function `matroxfb_g450_setpll_cond':
(.text+0x230): multiple definition of `matroxfb_g450_setpll_cond'; drivers/video/fbdev/matrox/g450_pll.o:(.text+0x230): first defined here
ld: drivers/staging/mgakms/g450_pll.o: in function `__crc_matroxfb_g450_setpll_cond': (*ABS*+0xc16eb42d): multiple definition of `__crc_matroxfb_g450_setpll_cond' ld: drivers/staging/mgakms/matroxfb_accel.o: in function `matrox_cfbX_init':
(.text+0x0): multiple definition of `matrox_cfbX_init'; drivers/video/fbdev/matrox/matroxfb_accel.o:(.text+0x0): first defined here
ld: drivers/staging/mgakms/matroxfb_accel.o: in function `__crc_matrox_cfbX_init': (*ABS*+0xad1ca004): multiple definition of `__crc_matrox_cfbX_init' ld: drivers/staging/mgakms/matroxfb_base.o: in function `__crc_matroxfb_enable_irq': (*ABS*+0x2f4ea06b): multiple definition of `__crc_matroxfb_enable_irq' ld: drivers/staging/mgakms/matroxfb_base.o: in function `__crc_matroxfb_register_driver': (*ABS*+0x9eefe8ea): multiple definition of `__crc_matroxfb_register_driver' ld: drivers/staging/mgakms/matroxfb_base.o: in function `matroxfb_enable_irq':
(.text+0x3d70): multiple definition of `matroxfb_enable_irq'; drivers/video/fbdev/matrox/matroxfb_base.o:(.text+0x3d40): first defined here
ld: drivers/staging/mgakms/matroxfb_base.o: in function `__crc_matroxfb_unregister_driver': (*ABS*+0xe6ed497): multiple definition of `__crc_matroxfb_unregister_driver' ld: drivers/staging/mgakms/matroxfb_base.o: in function `matroxfb_register_driver':
(.text+0x7e0): multiple definition of `matroxfb_register_driver'; drivers/video/fbdev/matrox/matroxfb_base.o:(.text+0x7e0): first defined here
ld: drivers/staging/mgakms/matroxfb_base.o: in function `matroxfb_wait_for_sync':
(.text+0x41b0): multiple definition of `matroxfb_wait_for_sync'; drivers/video/fbdev/matrox/matroxfb_base.o:(.text+0x4180): first defined here
ld: drivers/staging/mgakms/matroxfb_base.o: in function `__crc_matroxfb_wait_for_sync': (*ABS*+0xf264a0a8): multiple definition of `__crc_matroxfb_wait_for_sync' ld: drivers/staging/mgakms/matroxfb_base.o: in function `matroxfb_unregister_driver':
(.text+0x950): multiple definition of `matroxfb_unregister_driver'; drivers/video/fbdev/matrox/matroxfb_base.o:(.text+0x950): first defined here ld: drivers/staging/mgakms/matroxfb_DAC1064.o:(.data+0x0): multiple definition of `matrox_G100'; drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.data+0x0): first defined here
ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `__crc_DAC1064_global_init': (*ABS*+0x279aabee): multiple definition of `__crc_DAC1064_global_init' ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `__crc_DAC1064_global_restore': (*ABS*+0xb0a182ca): multiple definition of `__crc_DAC1064_global_restore'
ld: drivers/staging/mgakms/matroxfb_DAC1064.o:(.data+0x40): multiple definition of `matrox_mystique'; drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.data+0x40): first defined here
ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `__crc_matrox_mystique': (*ABS*+0x82a67894): multiple definition of `__crc_matrox_mystique' ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `DAC1064_global_restore':
(.text+0x0): multiple definition of `DAC1064_global_restore'; drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.text+0x0): first defined here
ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `__crc_matrox_G100': (*ABS*+0x1efba743): multiple definition of `__crc_matrox_G100' ld: drivers/staging/mgakms/matroxfb_DAC1064.o: in function `DAC1064_global_init':
(.text+0x130): multiple definition of `DAC1064_global_init'; drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.text+0x130): first defined here
ld: drivers/staging/mgakms/matroxfb_g450.o: in function `__crc_matroxfb_g450_connect': (*ABS*+0xb554d1f9): multiple definition of `__crc_matroxfb_g450_connect' ld: drivers/staging/mgakms/matroxfb_g450.o: in function `__crc_matroxfb_g450_shutdown': (*ABS*+0xfef8ad0b): multiple definition of `__crc_matroxfb_g450_shutdown' ld: drivers/staging/mgakms/matroxfb_g450.o: in function `matroxfb_g450_shutdown':
(.text+0x2c0): multiple definition of `matroxfb_g450_shutdown'; drivers/video/fbdev/matrox/matroxfb_g450.o:(.text+0x2c0): first defined here
ld: drivers/staging/mgakms/matroxfb_g450.o: in function `matroxfb_g450_connect':
(.text+0x130): multiple definition of `matroxfb_g450_connect'; drivers/video/fbdev/matrox/matroxfb_g450.o:(.text+0x130): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `__crc_matroxfb_DAC_in': (*ABS*+0xa94ac221): multiple definition of `__crc_matroxfb_DAC_in' ld: drivers/staging/mgakms/matroxfb_misc.o: in function `__crc_matroxfb_DAC_out': (*ABS*+0x7b927ef5): multiple definition of `__crc_matroxfb_DAC_out' ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_PLL_calcclock':
(.text+0x260): multiple definition of `matroxfb_PLL_calcclock'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0x260): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_DAC_in':
(.text+0x50): multiple definition of `matroxfb_DAC_in'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0x50): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_DAC_out':
(.text+0x0): multiple definition of `matroxfb_DAC_out'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0x0): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_vgaHWinit':
(.text+0x500): multiple definition of `matroxfb_vgaHWinit'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0x500): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `__crc_matroxfb_vgaHWinit': (*ABS*+0x880c2f5e): multiple definition of `__crc_matroxfb_vgaHWinit' ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_vgaHWrestore':
(.text+0xd50): multiple definition of `matroxfb_vgaHWrestore'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0xd50): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `__crc_matroxfb_read_pins': (*ABS*+0x10a9907b): multiple definition of `__crc_matroxfb_read_pins' ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_read_pins':
(.text+0x13d0): multiple definition of `matroxfb_read_pins'; drivers/video/fbdev/matrox/matroxfb_misc.o:(.text+0x13d0): first defined here
ld: drivers/staging/mgakms/matroxfb_misc.o: in function `matroxfb_var2my':
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Thomas,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master] [cannot apply to v5.4-rc3 next-20191014] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Thomas-Zimmermann/DRM-fbconv-helper... config: arm64-allyesconfig (attached as .config) compiler: aarch64-linux-gcc (GCC) 7.4.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree GCC_VERSION=7.4.0 make.cross ARCH=arm64
If you fix the issue, kindly add following tag Reported-by: kbuild test robot lkp@intel.com
All errors (new ones prefixed by >>):
drivers/staging/mgakms/g450_pll.o: In function `__crc_g450_mnp2f':
g450_pll.c:(*ABS*+0x3559243b): multiple definition of `__crc_g450_mnp2f'
drivers/staging/mgakms/g450_pll.o: In function `__crc_matroxfb_g450_setclk':
g450_pll.c:(*ABS*+0xb4e884b9): multiple definition of `__crc_matroxfb_g450_setclk'
drivers/staging/mgakms/g450_pll.o: In function `matroxfb_g450_setclk':
g450_pll.c:(.text+0x1fe8): multiple definition of `matroxfb_g450_setclk'
drivers/video/fbdev/matrox/g450_pll.o:g450_pll.c:(.text+0x1fe8): first defined here drivers/staging/mgakms/g450_pll.o: In function `g450_mnp2f':
g450_pll.c:(.text+0x0): multiple definition of `g450_mnp2f'
drivers/video/fbdev/matrox/g450_pll.o:g450_pll.c:(.text+0x0): first defined here drivers/staging/mgakms/g450_pll.o: In function `matroxfb_g450_setpll_cond':
g450_pll.c:(.text+0x258): multiple definition of `matroxfb_g450_setpll_cond'
drivers/video/fbdev/matrox/g450_pll.o:g450_pll.c:(.text+0x258): first defined here drivers/staging/mgakms/g450_pll.o: In function `__crc_matroxfb_g450_setpll_cond':
g450_pll.c:(*ABS*+0x92a49e90): multiple definition of `__crc_matroxfb_g450_setpll_cond'
drivers/staging/mgakms/matroxfb_accel.o: In function `matrox_cfbX_init':
matroxfb_accel.c:(.text+0x78): multiple definition of `matrox_cfbX_init'
drivers/video/fbdev/matrox/matroxfb_accel.o:matroxfb_accel.c:(.text+0x78): first defined here drivers/staging/mgakms/matroxfb_accel.o: In function `__crc_matrox_cfbX_init':
matroxfb_accel.c:(*ABS*+0x30cc4dd3): multiple definition of `__crc_matrox_cfbX_init'
drivers/staging/mgakms/matroxfb_base.o: In function `__crc_matroxfb_enable_irq':
matroxfb_base.c:(*ABS*+0xcf34e848): multiple definition of `__crc_matroxfb_enable_irq'
drivers/staging/mgakms/matroxfb_base.o: In function `__crc_matroxfb_register_driver':
matroxfb_base.c:(*ABS*+0x1294ef50): multiple definition of `__crc_matroxfb_register_driver'
drivers/staging/mgakms/matroxfb_base.o: In function `matroxfb_enable_irq':
matroxfb_base.c:(.text+0x1130): multiple definition of `matroxfb_enable_irq'
drivers/video/fbdev/matrox/matroxfb_base.o:matroxfb_base.c:(.text+0x1108): first defined here drivers/staging/mgakms/matroxfb_base.o: In function `__crc_matroxfb_unregister_driver':
matroxfb_base.c:(*ABS*+0x6a5fc443): multiple definition of `__crc_matroxfb_unregister_driver'
drivers/staging/mgakms/matroxfb_base.o: In function `matroxfb_register_driver':
matroxfb_base.c:(.text+0x3e8): multiple definition of `matroxfb_register_driver'
drivers/video/fbdev/matrox/matroxfb_base.o:matroxfb_base.c:(.text+0x3c8): first defined here drivers/staging/mgakms/matroxfb_base.o: In function `matroxfb_wait_for_sync':
matroxfb_base.c:(.text+0x13a0): multiple definition of `matroxfb_wait_for_sync'
drivers/video/fbdev/matrox/matroxfb_base.o:matroxfb_base.c:(.text+0x1378): first defined here drivers/staging/mgakms/matroxfb_base.o: In function `__crc_matroxfb_wait_for_sync':
matroxfb_base.c:(*ABS*+0x299ca154): multiple definition of `__crc_matroxfb_wait_for_sync'
drivers/staging/mgakms/matroxfb_base.o: In function `matroxfb_unregister_driver':
matroxfb_base.c:(.text+0x540): multiple definition of `matroxfb_unregister_driver'
drivers/video/fbdev/matrox/matroxfb_base.o:matroxfb_base.c:(.text+0x520): first defined here
drivers/staging/mgakms/matroxfb_DAC1064.o:(.data+0xc0): multiple definition of `matrox_G100'
drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.data+0xc0): first defined here drivers/staging/mgakms/matroxfb_DAC1064.o: In function `__crc_DAC1064_global_init':
matroxfb_DAC1064.c:(*ABS*+0xd101267): multiple definition of `__crc_DAC1064_global_init'
drivers/staging/mgakms/matroxfb_DAC1064.o: In function `__crc_DAC1064_global_restore':
matroxfb_DAC1064.c:(*ABS*+0xf73a4dac): multiple definition of `__crc_DAC1064_global_restore' drivers/staging/mgakms/matroxfb_DAC1064.o:(.data+0x100): multiple definition of `matrox_mystique'
drivers/video/fbdev/matrox/matroxfb_DAC1064.o:(.data+0x100): first defined here drivers/staging/mgakms/matroxfb_DAC1064.o: In function `__crc_matrox_mystique':
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Thomas.
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
In general I like the idea of modernizing the existing fbdev drivers. What I fail to read in your intro above is if this allows us to phase out the migrated fbdev drivers sooner? Or do we end up with two drivers to maintain?
Obviously a full migration to a DRM driver was preferred - but this may serve as a step in that direction. But we should not end up with two drivers doing almost the same.
Another general question. Do we want the modernized DRM drivers to end up in staging? Why should they not go direct into drm/* I know they are not fully atomic but this is not new drivers so maybe they can be excused. Problem is that drm drivers in staging live a secret nonvisible life where they are easy to forget when we change interfaces and such.
Sam
Hi
Am 14.10.19 um 22:36 schrieb Sam Ravnborg:
Hi Thomas.
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
In general I like the idea of modernizing the existing fbdev drivers. What I fail to read in your intro above is if this allows us to phase out the migrated fbdev drivers sooner? Or do we end up with two drivers to maintain?
The idea is that an fbdev driver is converted over to DRM and, once ready, the original fbdev driver gets removed. When a hybrid driver gets added, I'd want to see the rsp developer actually clean up and refactor the code. There shouldn't be multiple drivers for long.
But most of the fbdev drivers appear to be unmaintained anyway. I wouldn't expect having two drivers for a few releases would make much of a difference.
Obviously a full migration to a DRM driver was preferred - but this may serve as a step in that direction. But we should not end up with two drivers doing almost the same.
Another general question. Do we want the modernized DRM drivers to end up in staging? Why should they not go direct into drm/* I know they are not fully atomic but this is not new drivers so maybe they can be excused. Problem is that drm drivers in staging live a secret nonvisible life where they are easy to forget when we change interfaces and such.
True. OTOH putting them next to the regular DRM code sends the message that the driver is already complete and in good shape. Those hybrid drivers are limited in functionality and don't really live up to anyone's requirements for code quality. It's the kind of code one would expect in staging.
Best regards Thomas
Sam _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
- we've had conversion helpers before (for the legacy kms -> atomic upgrade). They constantly broke, pretty much every release when someone wanted to use them they first had to fix them up again. I think having those helpers is good, but better to just have them in some branch somewhere where it's clear that they might not work anymore on latest upstream.
- especially for some of these simple fbdev drivers I feel like just typing a new driver from scratch might be simpler.
A few more concerns specifically for your mga example:
- We already have a mga driver. Might be better to integrate support for older mgas into that than have a parallel driver.
- Your helper is based on simple display pipe, and I think for these old mga chips (especially the dual pipe mga 450 and 550) simple display pipe helper is more a hindering detour than actual help. From a quick read through the code (especially all the custom ioctls) you definitely want separate TV-out connector to expose all the tv mode properties (instead of the custom ioctls).
- On the topic of ioctls, looks like we could add FBIOGET_VBLANK to our generic implementation in the fbdev helpers.
So here's my alternative proposal:
- You push this as a branch onto a gitlab repo (freedesktop.org or wherever you feel like).
- You add a gitlab CI target to autobuild the very nice kerneldoc you've created. Feel free to also do this with anything else you're familiar with, it's just I know gitlab and it's real simple to get a few docs autogenerated and published with it.
- We add a todo.rst patch linking to your branch and the docs and a few lines on how to best convert an fbdev driver over to kms/atomic.
And all the drivers would land the usual way, like any of the other drivers we've added to drivers/gpu/drm over the past few years.
Thoughts?
Cheers, Daniel
v2:
- rename to fbconv helpers
- rewrite as helper library
- switch over to simple KMS helpers
- switch over to SHMEM
- add documentation
[1] https://gitlab.freedesktop.org/tzimmermann/linux/commits/fbconv-plus-drivers
Thomas Zimmermann (15): fbdev: Export fb_check_foreignness() fbdev: Export FBPIXMAPSIZE drm/simple-kms-helper: Add mode_fixup() to simple display pipe drm: Add fbconv helper module drm/fbconv: Add DRM <-> fbdev pixel-format conversion drm/fbconv: Add mode conversion DRM <-> fbdev drm/fbconv: Add modesetting infrastructure drm/fbconv: Add plane-state check and update drm/fbconv: Mode-setting pipeline enable / disable drm/fbconv: Reimplement several fbdev interfaces drm/fbconv: Add helpers for init and cleanup of fb_info structures drm/fbconv: Add helper documentation staging: Add mgakms driver staging/mgakms: Import matroxfb driver source code staging/mgakms: Update matroxfb driver code for DRM
Documentation/gpu/drm-kms-helpers.rst | 12 + Documentation/gpu/todo.rst | 15 + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 2126 +++++++++++++++++ drivers/gpu/drm/drm_simple_kms_helper.c | 15 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/mgakms/Kconfig | 18 + drivers/staging/mgakms/Makefile | 17 + drivers/staging/mgakms/g450_pll.c | 539 +++++ drivers/staging/mgakms/g450_pll.h | 13 + drivers/staging/mgakms/i2c-matroxfb.c | 238 ++ drivers/staging/mgakms/matroxfb_DAC1064.c | 1082 +++++++++ drivers/staging/mgakms/matroxfb_DAC1064.h | 174 ++ drivers/staging/mgakms/matroxfb_Ti3026.c | 746 ++++++ drivers/staging/mgakms/matroxfb_Ti3026.h | 10 + drivers/staging/mgakms/matroxfb_accel.c | 519 +++++ drivers/staging/mgakms/matroxfb_accel.h | 9 + drivers/staging/mgakms/matroxfb_base.c | 2592 +++++++++++++++++++++ drivers/staging/mgakms/matroxfb_base.h | 700 ++++++ drivers/staging/mgakms/matroxfb_crtc2.h | 35 + drivers/staging/mgakms/matroxfb_g450.c | 640 +++++ drivers/staging/mgakms/matroxfb_g450.h | 10 + drivers/staging/mgakms/matroxfb_maven.h | 21 + drivers/staging/mgakms/matroxfb_misc.c | 815 +++++++ drivers/staging/mgakms/matroxfb_misc.h | 22 + drivers/staging/mgakms/mga_device.c | 68 + drivers/staging/mgakms/mga_device.h | 30 + drivers/staging/mgakms/mga_drv.c | 129 + drivers/staging/mgakms/mga_drv.h | 14 + drivers/video/fbdev/core/fbmem.c | 5 +- include/drm/drm_fbconv_helper.h | 150 ++ include/drm/drm_simple_kms_helper.h | 43 + include/linux/fb.h | 3 + 35 files changed, 10822 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/drm_fbconv_helper.c create mode 100644 drivers/staging/mgakms/Kconfig create mode 100644 drivers/staging/mgakms/Makefile create mode 100644 drivers/staging/mgakms/g450_pll.c create mode 100644 drivers/staging/mgakms/g450_pll.h create mode 100644 drivers/staging/mgakms/i2c-matroxfb.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.h create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.c create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.h create mode 100644 drivers/staging/mgakms/matroxfb_accel.c create mode 100644 drivers/staging/mgakms/matroxfb_accel.h create mode 100644 drivers/staging/mgakms/matroxfb_base.c create mode 100644 drivers/staging/mgakms/matroxfb_base.h create mode 100644 drivers/staging/mgakms/matroxfb_crtc2.h create mode 100644 drivers/staging/mgakms/matroxfb_g450.c create mode 100644 drivers/staging/mgakms/matroxfb_g450.h create mode 100644 drivers/staging/mgakms/matroxfb_maven.h create mode 100644 drivers/staging/mgakms/matroxfb_misc.c create mode 100644 drivers/staging/mgakms/matroxfb_misc.h create mode 100644 drivers/staging/mgakms/mga_device.c create mode 100644 drivers/staging/mgakms/mga_device.h create mode 100644 drivers/staging/mgakms/mga_drv.c create mode 100644 drivers/staging/mgakms/mga_drv.h create mode 100644 include/drm/drm_fbconv_helper.h
-- 2.23.0
Hi Daniel
Am 15.10.19 um 16:33 schrieb Daniel Vetter:
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
I mostly put this into staging, because it's the kind of code you'd expect there.
we've had conversion helpers before (for the legacy kms -> atomic upgrade). They constantly broke, pretty much every release when someone wanted to use them they first had to fix them up again. I think having those helpers is good, but better to just have them in some branch somewhere where it's clear that they might not work anymore on latest upstream.
especially for some of these simple fbdev drivers I feel like just typing a new driver from scratch might be simpler.
A few more concerns specifically for your mga example:
- We already have a mga driver. Might be better to integrate support for older mgas into that than have a parallel driver.
Two colleagues of mine, Takashi and Egbert, send a patch that added support for desktop G200s to mgag200. [1] But it was rejected because the devices are two old and not relevant any longer. If that opinion has changed in the meantime, I wouldn't mind adding support for desktop GPUs to the driver.
- Your helper is based on simple display pipe, and I think for these old mga chips (especially the dual pipe mga 450 and 550) simple display pipe helper is more a hindering detour than actual help. From a quick read through the code (especially all the custom ioctls) you definitely want separate TV-out connector to expose all the tv mode properties (instead of the custom ioctls).
Around the G100, there's something like a change in generation. Before, devices had only a single output and less than 8 MiB of RAM. This works well with GEM SHMEM and simple KMS. Afterwards, devices have 8 MiB or more and multiple outputs. GEM VRAM and the full set of helpers fit this much better. Maybe having 2 drivers that share common code (or 3 with the Server Engine chipsets) makes most sense.
- On the topic of ioctls, looks like we could add FBIOGET_VBLANK to our generic implementation in the fbdev helpers.
So here's my alternative proposal:
You push this as a branch onto a gitlab repo (freedesktop.org or wherever you feel like).
You add a gitlab CI target to autobuild the very nice kerneldoc you've created. Feel free to also do this with anything else you're familiar with, it's just I know gitlab and it's real simple to get a few docs autogenerated and published with it.
We add a todo.rst patch linking to your branch and the docs and a few lines on how to best convert an fbdev driver over to kms/atomic.
Yes we can do that.
Best regards Thomas
[1] https://lists.freedesktop.org/archives/dri-devel/2017-July/147868.html
And all the drivers would land the usual way, like any of the other drivers we've added to drivers/gpu/drm over the past few years.
Thoughts?
Cheers, Daniel
v2:
- rename to fbconv helpers
- rewrite as helper library
- switch over to simple KMS helpers
- switch over to SHMEM
- add documentation
[1] https://gitlab.freedesktop.org/tzimmermann/linux/commits/fbconv-plus-drivers
Thomas Zimmermann (15): fbdev: Export fb_check_foreignness() fbdev: Export FBPIXMAPSIZE drm/simple-kms-helper: Add mode_fixup() to simple display pipe drm: Add fbconv helper module drm/fbconv: Add DRM <-> fbdev pixel-format conversion drm/fbconv: Add mode conversion DRM <-> fbdev drm/fbconv: Add modesetting infrastructure drm/fbconv: Add plane-state check and update drm/fbconv: Mode-setting pipeline enable / disable drm/fbconv: Reimplement several fbdev interfaces drm/fbconv: Add helpers for init and cleanup of fb_info structures drm/fbconv: Add helper documentation staging: Add mgakms driver staging/mgakms: Import matroxfb driver source code staging/mgakms: Update matroxfb driver code for DRM
Documentation/gpu/drm-kms-helpers.rst | 12 + Documentation/gpu/todo.rst | 15 + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 2126 +++++++++++++++++ drivers/gpu/drm/drm_simple_kms_helper.c | 15 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/mgakms/Kconfig | 18 + drivers/staging/mgakms/Makefile | 17 + drivers/staging/mgakms/g450_pll.c | 539 +++++ drivers/staging/mgakms/g450_pll.h | 13 + drivers/staging/mgakms/i2c-matroxfb.c | 238 ++ drivers/staging/mgakms/matroxfb_DAC1064.c | 1082 +++++++++ drivers/staging/mgakms/matroxfb_DAC1064.h | 174 ++ drivers/staging/mgakms/matroxfb_Ti3026.c | 746 ++++++ drivers/staging/mgakms/matroxfb_Ti3026.h | 10 + drivers/staging/mgakms/matroxfb_accel.c | 519 +++++ drivers/staging/mgakms/matroxfb_accel.h | 9 + drivers/staging/mgakms/matroxfb_base.c | 2592 +++++++++++++++++++++ drivers/staging/mgakms/matroxfb_base.h | 700 ++++++ drivers/staging/mgakms/matroxfb_crtc2.h | 35 + drivers/staging/mgakms/matroxfb_g450.c | 640 +++++ drivers/staging/mgakms/matroxfb_g450.h | 10 + drivers/staging/mgakms/matroxfb_maven.h | 21 + drivers/staging/mgakms/matroxfb_misc.c | 815 +++++++ drivers/staging/mgakms/matroxfb_misc.h | 22 + drivers/staging/mgakms/mga_device.c | 68 + drivers/staging/mgakms/mga_device.h | 30 + drivers/staging/mgakms/mga_drv.c | 129 + drivers/staging/mgakms/mga_drv.h | 14 + drivers/video/fbdev/core/fbmem.c | 5 +- include/drm/drm_fbconv_helper.h | 150 ++ include/drm/drm_simple_kms_helper.h | 43 + include/linux/fb.h | 3 + 35 files changed, 10822 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/drm_fbconv_helper.c create mode 100644 drivers/staging/mgakms/Kconfig create mode 100644 drivers/staging/mgakms/Makefile create mode 100644 drivers/staging/mgakms/g450_pll.c create mode 100644 drivers/staging/mgakms/g450_pll.h create mode 100644 drivers/staging/mgakms/i2c-matroxfb.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.h create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.c create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.h create mode 100644 drivers/staging/mgakms/matroxfb_accel.c create mode 100644 drivers/staging/mgakms/matroxfb_accel.h create mode 100644 drivers/staging/mgakms/matroxfb_base.c create mode 100644 drivers/staging/mgakms/matroxfb_base.h create mode 100644 drivers/staging/mgakms/matroxfb_crtc2.h create mode 100644 drivers/staging/mgakms/matroxfb_g450.c create mode 100644 drivers/staging/mgakms/matroxfb_g450.h create mode 100644 drivers/staging/mgakms/matroxfb_maven.h create mode 100644 drivers/staging/mgakms/matroxfb_misc.c create mode 100644 drivers/staging/mgakms/matroxfb_misc.h create mode 100644 drivers/staging/mgakms/mga_device.c create mode 100644 drivers/staging/mgakms/mga_device.h create mode 100644 drivers/staging/mgakms/mga_drv.c create mode 100644 drivers/staging/mgakms/mga_drv.h create mode 100644 include/drm/drm_fbconv_helper.h
-- 2.23.0
On Tue, Oct 15, 2019 at 7:28 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 15.10.19 um 16:33 schrieb Daniel Vetter:
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
I mostly put this into staging, because it's the kind of code you'd expect there.
Yeah, except we tried, it's a real pain. Conclusion by everyone involved is that staging doesn't work for the drm subsystem.
we've had conversion helpers before (for the legacy kms -> atomic upgrade). They constantly broke, pretty much every release when someone wanted to use them they first had to fix them up again. I think having those helpers is good, but better to just have them in some branch somewhere where it's clear that they might not work anymore on latest upstream.
especially for some of these simple fbdev drivers I feel like just typing a new driver from scratch might be simpler.
A few more concerns specifically for your mga example:
- We already have a mga driver. Might be better to integrate support for older mgas into that than have a parallel driver.
Two colleagues of mine, Takashi and Egbert, send a patch that added support for desktop G200s to mgag200. [1] But it was rejected because the devices are two old and not relevant any longer. If that opinion has changed in the meantime, I wouldn't mind adding support for desktop GPUs to the driver.
Hm that seems to have petered out inconclusive. I definitely think a merged mga driver is better than 2 drm atomic kms drivers for roughly the same hardware. I'm also assuming that at least for now no one plans to resurrect the 3d acceleration support for these old chips. But even then it's fairly easy to disable all that on the server chips.
- Your helper is based on simple display pipe, and I think for these old mga chips (especially the dual pipe mga 450 and 550) simple display pipe helper is more a hindering detour than actual help. From a quick read through the code (especially all the custom ioctls) you definitely want separate TV-out connector to expose all the tv mode properties (instead of the custom ioctls).
Around the G100, there's something like a change in generation. Before, devices had only a single output and less than 8 MiB of RAM. This works well with GEM SHMEM and simple KMS. Afterwards, devices have 8 MiB or more and multiple outputs. GEM VRAM and the full set of helpers fit this much better. Maybe having 2 drivers that share common code (or 3 with the Server Engine chipsets) makes most sense.
Yeah if that's the case maybe a mga100 and mga200g driver fits better. Former based on simple display pipe.
You could also have them in one directory/kernel module, if there's some low-level functions worth sharing (like clock programming), with the low level driver probe either setting up the simple display based mga100 support, or the fancier mga200 support using atomic directly. If there's really no shared code at all, then separate kernel modules sounds better.
- On the topic of ioctls, looks like we could add FBIOGET_VBLANK to our generic implementation in the fbdev helpers.
So here's my alternative proposal:
You push this as a branch onto a gitlab repo (freedesktop.org or wherever you feel like).
You add a gitlab CI target to autobuild the very nice kerneldoc you've created. Feel free to also do this with anything else you're familiar with, it's just I know gitlab and it's real simple to get a few docs autogenerated and published with it.
We add a todo.rst patch linking to your branch and the docs and a few lines on how to best convert an fbdev driver over to kms/atomic.
Yes we can do that.
Sounds like we have a rough plan? -Daniel
Best regards Thomas
[1] https://lists.freedesktop.org/archives/dri-devel/2017-July/147868.html
And all the drivers would land the usual way, like any of the other drivers we've added to drivers/gpu/drm over the past few years.
Thoughts?
Cheers, Daniel
v2: * rename to fbconv helpers * rewrite as helper library * switch over to simple KMS helpers * switch over to SHMEM * add documentation
[1] https://gitlab.freedesktop.org/tzimmermann/linux/commits/fbconv-plus-drivers
Thomas Zimmermann (15): fbdev: Export fb_check_foreignness() fbdev: Export FBPIXMAPSIZE drm/simple-kms-helper: Add mode_fixup() to simple display pipe drm: Add fbconv helper module drm/fbconv: Add DRM <-> fbdev pixel-format conversion drm/fbconv: Add mode conversion DRM <-> fbdev drm/fbconv: Add modesetting infrastructure drm/fbconv: Add plane-state check and update drm/fbconv: Mode-setting pipeline enable / disable drm/fbconv: Reimplement several fbdev interfaces drm/fbconv: Add helpers for init and cleanup of fb_info structures drm/fbconv: Add helper documentation staging: Add mgakms driver staging/mgakms: Import matroxfb driver source code staging/mgakms: Update matroxfb driver code for DRM
Documentation/gpu/drm-kms-helpers.rst | 12 + Documentation/gpu/todo.rst | 15 + drivers/gpu/drm/Kconfig | 11 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_fbconv_helper.c | 2126 +++++++++++++++++ drivers/gpu/drm/drm_simple_kms_helper.c | 15 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/mgakms/Kconfig | 18 + drivers/staging/mgakms/Makefile | 17 + drivers/staging/mgakms/g450_pll.c | 539 +++++ drivers/staging/mgakms/g450_pll.h | 13 + drivers/staging/mgakms/i2c-matroxfb.c | 238 ++ drivers/staging/mgakms/matroxfb_DAC1064.c | 1082 +++++++++ drivers/staging/mgakms/matroxfb_DAC1064.h | 174 ++ drivers/staging/mgakms/matroxfb_Ti3026.c | 746 ++++++ drivers/staging/mgakms/matroxfb_Ti3026.h | 10 + drivers/staging/mgakms/matroxfb_accel.c | 519 +++++ drivers/staging/mgakms/matroxfb_accel.h | 9 + drivers/staging/mgakms/matroxfb_base.c | 2592 +++++++++++++++++++++ drivers/staging/mgakms/matroxfb_base.h | 700 ++++++ drivers/staging/mgakms/matroxfb_crtc2.h | 35 + drivers/staging/mgakms/matroxfb_g450.c | 640 +++++ drivers/staging/mgakms/matroxfb_g450.h | 10 + drivers/staging/mgakms/matroxfb_maven.h | 21 + drivers/staging/mgakms/matroxfb_misc.c | 815 +++++++ drivers/staging/mgakms/matroxfb_misc.h | 22 + drivers/staging/mgakms/mga_device.c | 68 + drivers/staging/mgakms/mga_device.h | 30 + drivers/staging/mgakms/mga_drv.c | 129 + drivers/staging/mgakms/mga_drv.h | 14 + drivers/video/fbdev/core/fbmem.c | 5 +- include/drm/drm_fbconv_helper.h | 150 ++ include/drm/drm_simple_kms_helper.h | 43 + include/linux/fb.h | 3 + 35 files changed, 10822 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/drm_fbconv_helper.c create mode 100644 drivers/staging/mgakms/Kconfig create mode 100644 drivers/staging/mgakms/Makefile create mode 100644 drivers/staging/mgakms/g450_pll.c create mode 100644 drivers/staging/mgakms/g450_pll.h create mode 100644 drivers/staging/mgakms/i2c-matroxfb.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.c create mode 100644 drivers/staging/mgakms/matroxfb_DAC1064.h create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.c create mode 100644 drivers/staging/mgakms/matroxfb_Ti3026.h create mode 100644 drivers/staging/mgakms/matroxfb_accel.c create mode 100644 drivers/staging/mgakms/matroxfb_accel.h create mode 100644 drivers/staging/mgakms/matroxfb_base.c create mode 100644 drivers/staging/mgakms/matroxfb_base.h create mode 100644 drivers/staging/mgakms/matroxfb_crtc2.h create mode 100644 drivers/staging/mgakms/matroxfb_g450.c create mode 100644 drivers/staging/mgakms/matroxfb_g450.h create mode 100644 drivers/staging/mgakms/matroxfb_maven.h create mode 100644 drivers/staging/mgakms/matroxfb_misc.c create mode 100644 drivers/staging/mgakms/matroxfb_misc.h create mode 100644 drivers/staging/mgakms/mga_device.c create mode 100644 drivers/staging/mgakms/mga_device.h create mode 100644 drivers/staging/mgakms/mga_drv.c create mode 100644 drivers/staging/mgakms/mga_drv.h create mode 100644 include/drm/drm_fbconv_helper.h
-- 2.23.0
-- Thomas Zimmermann Graphics Driver Developer SUSE Software Solutions Germany GmbH Maxfeldstr. 5, 90409 Nürnberg, Germany (HRB 36809, AG Nürnberg) Geschäftsführer: Felix Imendörffer
On Tue, Oct 15, 2019 at 07:48:29PM +0200, Daniel Vetter wrote:
On Tue, Oct 15, 2019 at 7:28 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 15.10.19 um 16:33 schrieb Daniel Vetter:
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
I mostly put this into staging, because it's the kind of code you'd expect there.
Yeah, except we tried, it's a real pain. Conclusion by everyone involved is that staging doesn't work for the drm subsystem.
As the staging maintainer, I too agree with this. No drm drivers/code in staging please.
thanks,
greg k-h
On Tue, Oct 15, 2019 at 07:48:29PM +0200, Daniel Vetter wrote:
On Tue, Oct 15, 2019 at 7:28 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 15.10.19 um 16:33 schrieb Daniel Vetter:
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
I mostly put this into staging, because it's the kind of code you'd expect there.
Yeah, except we tried, it's a real pain. Conclusion by everyone involved is that staging doesn't work for the drm subsystem.
we've had conversion helpers before (for the legacy kms -> atomic upgrade). They constantly broke, pretty much every release when someone wanted to use them they first had to fix them up again. I think having those helpers is good, but better to just have them in some branch somewhere where it's clear that they might not work anymore on latest upstream.
especially for some of these simple fbdev drivers I feel like just typing a new driver from scratch might be simpler.
A few more concerns specifically for your mga example:
- We already have a mga driver. Might be better to integrate support for older mgas into that than have a parallel driver.
Two colleagues of mine, Takashi and Egbert, send a patch that added support for desktop G200s to mgag200. [1] But it was rejected because the devices are two old and not relevant any longer. If that opinion has changed in the meantime, I wouldn't mind adding support for desktop GPUs to the driver.
Hm that seems to have petered out inconclusive. I definitely think a merged mga driver is better than 2 drm atomic kms drivers for roughly the same hardware. I'm also assuming that at least for now no one plans to resurrect the 3d acceleration support for these old chips. But even then it's fairly easy to disable all that on the server chips.
- Your helper is based on simple display pipe, and I think for these old mga chips (especially the dual pipe mga 450 and 550) simple display pipe helper is more a hindering detour than actual help. From a quick read through the code (especially all the custom ioctls) you definitely want separate TV-out connector to expose all the tv mode properties (instead of the custom ioctls).
Around the G100, there's something like a change in generation. Before, devices had only a single output and less than 8 MiB of RAM. This works well with GEM SHMEM and simple KMS. Afterwards, devices have 8 MiB or more and multiple outputs. GEM VRAM and the full set of helpers fit this much better. Maybe having 2 drivers that share common code (or 3 with the Server Engine chipsets) makes most sense.
Yeah if that's the case maybe a mga100 and mga200g driver fits better. Former based on simple display pipe.
The display hardware differences are quite minimal from 1064SG to G200. G400 did add the second CRTC but essentially nothing else changed from G200 display. G450/G550 changed the PLLS around a bit just for the heck of it, and integrated the TMDS transmitter and TV encoder. And then they did even more PLL madness with the different G200 server chip variants.
So IMO from display hw POV G100 vs. G200 split doesn't really make sense.
And if you go to the effort of supporting dual head on G400+ then the single head case on earlier platforms is just the same as the single head "trivial" case on G400+, so totally different drivers doesn't make sense there either. Also not all G400 cards have the TVO/TMDS chip present in which case you can only use a single head anyway (it's almost identical to a G200 card at that point, assuming you drive the single output with the primary CRTC).
On Tue, Oct 15, 2019 at 09:13:37PM +0300, Ville Syrjälä wrote:
On Tue, Oct 15, 2019 at 07:48:29PM +0200, Daniel Vetter wrote:
On Tue, Oct 15, 2019 at 7:28 PM Thomas Zimmermann tzimmermann@suse.de wrote:
Hi Daniel
Am 15.10.19 um 16:33 schrieb Daniel Vetter:
Hi Thomas,
On Mon, Oct 14, 2019 at 04:04:01PM +0200, Thomas Zimmermann wrote:
(was: DRM driver for fbdev devices)
This is version 2 of the fbdev conversion helpers. It's more or less a rewrite of the original patchset.
The fbdev subsystem is considered legacy and will probably be removed at some point. This would mean the loss of a signifanct number of drivers. Some of the affected hardware is not in use any longer, but some hardware is still around and provides good(-enough) framebuffers.
The fbconv helpers allow for running the current DRM stack on top of fbdev drivers. It's a set of functions that convert between fbdev interfaces and DRM interfaces. Based on SHMEM and simple KMS helpers, it only offers the basic functionality of a framebuffer, but should be compatible with most existing fbdev drivers.
A DRM driver using fbconv helpers consists of
- DRM stub code that calls into fbconv helpers, and
- the original fbdev driver code.
The fbdev driver code has to be modified to register itself with the stub driver instead of the fbdev core framework. A tutorial on how to use the helpers is part of this patchset. The resulting driver hybrid can be refactored into a first-class DRM driver. The fbconv helpers contain a number of comments, labeled 'DRM porting note', which explain the required steps.
I tested the current patchset with the following drivers: atyfb, aty128fb, matroxfb, pm2fb, pm3fb, rivafb, s3fb, savagefb, sisfb, tdfxfb and tridentfb. With each, I was able to successfully start with fbcon enabled, run weston and X11. The drivers are available at [1]. For reference, the patchset includes the Matrox stub driver.
So I really don't want to rain on the parade here, since if you think this is useful when converting fbdev drivers I'll buy that, and I'm all for getting more modern drivers into drm.
But I have a bunch of concerns with the approach you're proposing here:
- we've tried staging for drm driver refactoring, it hurts. Separate tree plus the quick pace in refactoring create lots of pains. And for small drivers refacotoring before it's not buying you anything above refactoring in your own personal tree. And for big drivers we're fairly lenient with merging drivers that aren't fully polished yet, if there's a team serious enough with cleaning up the mess. I think even merging partial drivers directly under drivers/gpu (but behind CONFIG_BROKEN) is better than staging.
I mostly put this into staging, because it's the kind of code you'd expect there.
Yeah, except we tried, it's a real pain. Conclusion by everyone involved is that staging doesn't work for the drm subsystem.
we've had conversion helpers before (for the legacy kms -> atomic upgrade). They constantly broke, pretty much every release when someone wanted to use them they first had to fix them up again. I think having those helpers is good, but better to just have them in some branch somewhere where it's clear that they might not work anymore on latest upstream.
especially for some of these simple fbdev drivers I feel like just typing a new driver from scratch might be simpler.
A few more concerns specifically for your mga example:
- We already have a mga driver. Might be better to integrate support for older mgas into that than have a parallel driver.
Two colleagues of mine, Takashi and Egbert, send a patch that added support for desktop G200s to mgag200. [1] But it was rejected because the devices are two old and not relevant any longer. If that opinion has changed in the meantime, I wouldn't mind adding support for desktop GPUs to the driver.
Hm that seems to have petered out inconclusive. I definitely think a merged mga driver is better than 2 drm atomic kms drivers for roughly the same hardware. I'm also assuming that at least for now no one plans to resurrect the 3d acceleration support for these old chips. But even then it's fairly easy to disable all that on the server chips.
- Your helper is based on simple display pipe, and I think for these old mga chips (especially the dual pipe mga 450 and 550) simple display pipe helper is more a hindering detour than actual help. From a quick read through the code (especially all the custom ioctls) you definitely want separate TV-out connector to expose all the tv mode properties (instead of the custom ioctls).
Around the G100, there's something like a change in generation. Before, devices had only a single output and less than 8 MiB of RAM. This works well with GEM SHMEM and simple KMS. Afterwards, devices have 8 MiB or more and multiple outputs. GEM VRAM and the full set of helpers fit this much better. Maybe having 2 drivers that share common code (or 3 with the Server Engine chipsets) makes most sense.
Yeah if that's the case maybe a mga100 and mga200g driver fits better. Former based on simple display pipe.
The display hardware differences are quite minimal from 1064SG to G200. G400 did add the second CRTC but essentially nothing else changed from G200 display. G450/G550 changed the PLLS around a bit just for the heck of it, and integrated the TMDS transmitter and TV encoder. And then they did even more PLL madness with the different G200 server chip variants.
So IMO from display hw POV G100 vs. G200 split doesn't really make sense.
Ah, I did forget that G100 and earlier don't support the cursor 16 color mode that G200+ have. So I guess there is a little bit of a difference there.
dri-devel@lists.freedesktop.org