1/* $NetBSD: mga_warp.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $ */ 2 3/* mga_warp.c -- Matrox G200/G400 WARP engine management -*- linux-c -*- 4 * Created: Thu Jan 11 21:29:32 2001 by gareth@valinux.com 5 * 6 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 7 * All Rights Reserved. 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the next 17 * paragraph) shall be included in all copies or substantial portions of the 18 * Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 * 28 * Authors: 29 * Gareth Hughes <gareth@valinux.com> 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: mga_warp.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $"); 34 35#include <linux/firmware.h> 36#include <linux/ihex.h> 37#include <linux/module.h> 38#include <linux/platform_device.h> 39 40#include "mga_drv.h" 41 42#define FIRMWARE_G200 "matrox/g200_warp.fw" 43#define FIRMWARE_G400 "matrox/g400_warp.fw" 44 45MODULE_FIRMWARE(FIRMWARE_G200); 46MODULE_FIRMWARE(FIRMWARE_G400); 47 48#define MGA_WARP_CODE_ALIGN 256 /* in bytes */ 49 50#define WARP_UCODE_SIZE(size) ALIGN(size, MGA_WARP_CODE_ALIGN) 51 52int mga_warp_install_microcode(drm_mga_private_t *dev_priv) 53{ 54 unsigned char *vcbase = dev_priv->warp->handle; 55 unsigned long pcbase = dev_priv->warp->offset; 56 const char *firmware_name; 57 struct platform_device *pdev; 58 const struct firmware *fw = NULL; 59 const struct ihex_binrec *rec; 60 unsigned int size; 61 int n_pipes, where; 62 int rc = 0; 63 64 switch (dev_priv->chipset) { 65 case MGA_CARD_TYPE_G400: 66 case MGA_CARD_TYPE_G550: 67 firmware_name = FIRMWARE_G400; 68 n_pipes = MGA_MAX_G400_PIPES; 69 break; 70 case MGA_CARD_TYPE_G200: 71 firmware_name = FIRMWARE_G200; 72 n_pipes = MGA_MAX_G200_PIPES; 73 break; 74 default: 75 return -EINVAL; 76 } 77 78 pdev = platform_device_register_simple("mga_warp", 0, NULL, 0); 79 if (IS_ERR(pdev)) { 80 DRM_ERROR("mga: Failed to register microcode\n"); 81 return PTR_ERR(pdev); 82 } 83 rc = request_ihex_firmware(&fw, firmware_name, &pdev->dev); 84 platform_device_unregister(pdev); 85 if (rc) { 86 DRM_ERROR("mga: Failed to load microcode \"%s\"\n", 87 firmware_name); 88 return rc; 89 } 90 91 size = 0; 92 where = 0; 93 for (rec = (const struct ihex_binrec *)fw->data; 94 rec; 95 rec = ihex_next_binrec(rec)) { 96 size += WARP_UCODE_SIZE(be16_to_cpu(rec->len)); 97 where++; 98 } 99 100 if (where != n_pipes) { 101 DRM_ERROR("mga: Invalid microcode \"%s\"\n", firmware_name); 102 rc = -EINVAL; 103 goto out; 104 } 105 size = PAGE_ALIGN(size); 106 DRM_DEBUG("MGA ucode size = %d bytes\n", size); 107 if (size > dev_priv->warp->size) { 108 DRM_ERROR("microcode too large! (%u > %lu)\n", 109 size, dev_priv->warp->size); 110 rc = -ENOMEM; 111 goto out; 112 } 113 114 memset(dev_priv->warp_pipe_phys, 0, sizeof(dev_priv->warp_pipe_phys)); 115 116 where = 0; 117 for (rec = (const struct ihex_binrec *)fw->data; 118 rec; 119 rec = ihex_next_binrec(rec)) { 120 unsigned int src_size, dst_size; 121 122 DRM_DEBUG(" pcbase = 0x%08lx vcbase = %p\n", pcbase, vcbase); 123 dev_priv->warp_pipe_phys[where] = pcbase; 124 src_size = be16_to_cpu(rec->len); 125 dst_size = WARP_UCODE_SIZE(src_size); 126 memcpy(vcbase, rec->data, src_size); 127 pcbase += dst_size; 128 vcbase += dst_size; 129 where++; 130 } 131 132out: 133 release_firmware(fw); 134 return rc; 135} 136 137#define WMISC_EXPECTED (MGA_WUCODECACHE_ENABLE | MGA_WMASTER_ENABLE) 138 139int mga_warp_init(drm_mga_private_t *dev_priv) 140{ 141 u32 wmisc; 142 143 /* FIXME: Get rid of these damned magic numbers... 144 */ 145 switch (dev_priv->chipset) { 146 case MGA_CARD_TYPE_G400: 147 case MGA_CARD_TYPE_G550: 148 MGA_WRITE(MGA_WIADDR2, MGA_WMODE_SUSPEND); 149 MGA_WRITE(MGA_WGETMSB, 0x00000E00); 150 MGA_WRITE(MGA_WVRTXSZ, 0x00001807); 151 MGA_WRITE(MGA_WACCEPTSEQ, 0x18000000); 152 break; 153 case MGA_CARD_TYPE_G200: 154 MGA_WRITE(MGA_WIADDR, MGA_WMODE_SUSPEND); 155 MGA_WRITE(MGA_WGETMSB, 0x1606); 156 MGA_WRITE(MGA_WVRTXSZ, 7); 157 break; 158 default: 159 return -EINVAL; 160 } 161 162 MGA_WRITE(MGA_WMISC, (MGA_WUCODECACHE_ENABLE | 163 MGA_WMASTER_ENABLE | MGA_WCACHEFLUSH_ENABLE)); 164 wmisc = MGA_READ(MGA_WMISC); 165 if (wmisc != WMISC_EXPECTED) { 166 DRM_ERROR("WARP engine config failed! 0x%x != 0x%x\n", 167 wmisc, WMISC_EXPECTED); 168 return -EINVAL; 169 } 170 171 return 0; 172} 173