1/*
2	The all defines and part of code (such as cs461x_*) are
3	contributed from ALSA 0.5.8 sources.
4	See http://www.alsa-project.org/ for sources
5
6	Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610
7*/
8
9#include <asm/io.h>
10
11#include <linux/module.h>
12#include <linux/ioport.h>
13#include <linux/config.h>
14#include <linux/init.h>
15#include <linux/gameport.h>
16#include <linux/slab.h>
17#include <linux/pci.h>
18
19MODULE_AUTHOR("Victor Krapivin <vik@belcaf.minsk.by>");
20MODULE_LICENSE("GPL");
21
22/*
23	These options are experimental
24
25#define CS461X_FULL_MAP
26*/
27
28#define COOKED_MODE
29
30
31#ifndef PCI_VENDOR_ID_CIRRUS
32#define PCI_VENDOR_ID_CIRRUS            0x1013
33#endif
34#ifndef PCI_DEVICE_ID_CIRRUS_4610
35#define PCI_DEVICE_ID_CIRRUS_4610       0x6001
36#endif
37#ifndef PCI_DEVICE_ID_CIRRUS_4612
38#define PCI_DEVICE_ID_CIRRUS_4612       0x6003
39#endif
40#ifndef PCI_DEVICE_ID_CIRRUS_4615
41#define PCI_DEVICE_ID_CIRRUS_4615       0x6004
42#endif
43
44/* Registers */
45
46#define BA0_JSPT                                0x00000480
47#define BA0_JSCTL                               0x00000484
48#define BA0_JSC1                                0x00000488
49#define BA0_JSC2                                0x0000048C
50#define BA0_JSIO                                0x000004A0
51
52/* Bits for JSPT */
53
54#define JSPT_CAX                                0x00000001
55#define JSPT_CAY                                0x00000002
56#define JSPT_CBX                                0x00000004
57#define JSPT_CBY                                0x00000008
58#define JSPT_BA1                                0x00000010
59#define JSPT_BA2                                0x00000020
60#define JSPT_BB1                                0x00000040
61#define JSPT_BB2                                0x00000080
62
63/* Bits for JSCTL */
64
65#define JSCTL_SP_MASK                           0x00000003
66#define JSCTL_SP_SLOW                           0x00000000
67#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
68#define JSCTL_SP_MEDIUM_FAST                    0x00000002
69#define JSCTL_SP_FAST                           0x00000003
70#define JSCTL_ARE                               0x00000004
71
72/* Data register pairs masks */
73
74#define JSC1_Y1V_MASK                           0x0000FFFF
75#define JSC1_X1V_MASK                           0xFFFF0000
76#define JSC1_Y1V_SHIFT                          0
77#define JSC1_X1V_SHIFT                          16
78#define JSC2_Y2V_MASK                           0x0000FFFF
79#define JSC2_X2V_MASK                           0xFFFF0000
80#define JSC2_Y2V_SHIFT                          0
81#define JSC2_X2V_SHIFT                          16
82
83/* JS GPIO */
84
85#define JSIO_DAX                                0x00000001
86#define JSIO_DAY                                0x00000002
87#define JSIO_DBX                                0x00000004
88#define JSIO_DBY                                0x00000008
89#define JSIO_AXOE                               0x00000010
90#define JSIO_AYOE                               0x00000020
91#define JSIO_BXOE                               0x00000040
92#define JSIO_BYOE                               0x00000080
93
94/*
95   The card initialization code is obfuscated; the module cs461x
96   need to be loaded after ALSA modules initialized and something
97   played on the CS 4610 chip (see sources for details of CS4610
98   initialization code from ALSA)
99*/
100
101/* Card specific definitions */
102
103#define CS461X_BA0_SIZE         0x2000
104#define CS461X_BA1_DATA0_SIZE   0x3000
105#define CS461X_BA1_DATA1_SIZE   0x3800
106#define CS461X_BA1_PRG_SIZE     0x7000
107#define CS461X_BA1_REG_SIZE     0x0100
108
109#define BA1_SP_DMEM0                            0x00000000
110#define BA1_SP_DMEM1                            0x00010000
111#define BA1_SP_PMEM                             0x00020000
112#define BA1_SP_REG                              0x00030000
113
114#define BA1_DWORD_SIZE          (13 * 1024 + 512)
115#define BA1_MEMORY_COUNT        3
116
117/*
118   Only one CS461x card is still suppoted; the code requires
119   redesign to avoid this limitatuion.
120*/
121
122static unsigned long ba0_addr;
123static unsigned int *ba0;
124
125#ifdef CS461X_FULL_MAP
126static unsigned long ba1_addr;
127static union ba1_t {
128        struct {
129                unsigned int *data0;
130                unsigned int *data1;
131                unsigned int *pmem;
132                unsigned int *reg;
133        } name;
134        unsigned int *idx[4];
135} ba1;
136
137static void cs461x_poke(unsigned long reg, unsigned int val)
138{
139        ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff] = val;
140}
141
142static unsigned int cs461x_peek(unsigned long reg)
143{
144        return ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff];
145}
146
147#endif
148
149static void cs461x_pokeBA0(unsigned long reg, unsigned int val)
150{
151        ba0[reg >> 2] = val;
152}
153
154static unsigned int cs461x_peekBA0(unsigned long reg)
155{
156        return ba0[reg >> 2];
157}
158
159static int cs461x_free(struct pci_dev *pdev)
160{
161	struct gameport *port = pci_get_drvdata(pdev);
162	if(port){
163	    gameport_unregister_port(port);
164	    kfree(port);
165	}
166	if (ba0) iounmap(ba0);
167#ifdef CS461X_FULL_MAP
168	if (ba1.name.data0) iounmap(ba1.name.data0);
169	if (ba1.name.data1) iounmap(ba1.name.data1);
170	if (ba1.name.pmem)  iounmap(ba1.name.pmem);
171	if (ba1.name.reg)   iounmap(ba1.name.reg);
172#endif
173	return 0;
174}
175
176static void cs461x_gameport_trigger(struct gameport *gameport)
177{
178	cs461x_pokeBA0(BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);
179}
180
181static unsigned char cs461x_gameport_read(struct gameport *gameport)
182{
183	return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io);
184}
185
186static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
187{
188	unsigned js1, js2, jst;
189
190	js1 = cs461x_peekBA0(BA0_JSC1);
191	js2 = cs461x_peekBA0(BA0_JSC2);
192	jst = cs461x_peekBA0(BA0_JSPT);
193
194	*buttons = (~jst >> 4) & 0x0F;
195
196	axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
197	axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
198	axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
199	axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
200
201	for(jst=0;jst<4;++jst)
202		if(axes[jst]==0xFFFF) axes[jst] = -1;
203	return 0;
204}
205
206static int cs461x_gameport_open(struct gameport *gameport, int mode)
207{
208	switch (mode) {
209#ifdef COOKED_MODE
210	case GAMEPORT_MODE_COOKED:
211		return 0;
212#endif
213	case GAMEPORT_MODE_RAW:
214		return 0;
215	default:
216		return -1;
217	}
218	return 0;
219}
220
221static struct pci_device_id cs461x_pci_tbl[] __devinitdata = {
222	{ PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */
223	{ PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */
224	{ PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */
225	{ 0, }
226};
227MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl);
228
229static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
230{
231	int rc;
232	struct gameport* port;
233
234	rc = pci_enable_device(pdev);
235	if (rc) {
236		printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n",
237			pdev->bus->number, pdev->devfn, rc);
238		return rc;
239	}
240
241	ba0_addr = pci_resource_start(pdev, 0);
242#ifdef CS461X_FULL_MAP
243	ba1_addr = pci_resource_start(pdev, 1);
244#endif
245	if (ba0_addr == 0 || ba0_addr == ~0
246#ifdef CS461X_FULL_MAP
247            || ba1_addr == 0 || ba1_addr == ~0
248#endif
249	    ) {
250                printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr);
251#ifdef CS461X_FULL_MAP
252                printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr);
253#endif
254		cs461x_free(pdev);
255                return -ENOMEM;
256        }
257
258	ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE);
259#ifdef CS461X_FULL_MAP
260	ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
261	ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
262	ba1.name.pmem  = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
263	ba1.name.reg   = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
264
265	if (ba0 == NULL || ba1.name.data0 == NULL ||
266            ba1.name.data1 == NULL || ba1.name.pmem == NULL ||
267            ba1.name.reg == NULL) {
268		cs461x_free(pdev);
269                return -ENOMEM;
270        }
271#else
272	if (ba0 == NULL){
273		cs461x_free(pdev);
274		return -ENOMEM;
275	}
276#endif
277	printk(KERN_INFO "CS461x PCI: %lx[%d]\n",
278	    ba0_addr, CS461X_BA0_SIZE);
279
280	if (!(port = kmalloc(sizeof(struct gameport), GFP_KERNEL))) {
281		printk(KERN_ERR "Memory allocation failed.\n");
282		cs461x_free(pdev);
283		return -ENOMEM;
284	}
285	memset(port, 0, sizeof(struct gameport));
286
287	pci_set_drvdata(pdev, port);
288
289	port->open = cs461x_gameport_open;
290	port->read = cs461x_gameport_read;
291	port->trigger = cs461x_gameport_trigger;
292#ifdef COOKED_MODE
293	port->cooked_read = cs461x_gameport_cooked_read;
294#endif
295
296	cs461x_pokeBA0(BA0_JSIO, 0xFF); // ?
297	cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
298
299	gameport_register_port(port);
300
301	printk(KERN_INFO "gameport%d: CS461x Gameport speed %d kHz\n",
302		port->number, port->speed);
303
304	return 0;
305}
306
307static void __devexit cs461x_pci_remove(struct pci_dev *pdev)
308{
309	cs461x_free(pdev);
310}
311
312static struct pci_driver cs461x_pci_driver = {
313        name:           "PCI Gameport",
314        id_table:       cs461x_pci_tbl,
315        probe:          cs461x_pci_probe,
316        remove:         __devexit_p(cs461x_pci_remove),
317};
318
319int __init js_cs461x_init(void)
320{
321        return pci_module_init(&cs461x_pci_driver);
322}
323
324void __exit js_cs461x_exit(void)
325{
326        pci_unregister_driver(&cs461x_pci_driver);
327}
328
329module_init(js_cs461x_init);
330module_exit(js_cs461x_exit);
331
332