1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
29/*	All Rights Reserved	*/
30
31#include <sys/errno.h>
32#include <sys/types.h>
33#include <sys/conf.h>
34#include <sys/kmem.h>
35#include <sys/visual_io.h>
36#include <sys/font.h>
37#include <sys/fbio.h>
38
39#include <sys/ddi.h>
40#include <sys/stat.h>
41#include <sys/sunddi.h>
42#include <sys/file.h>
43#include <sys/open.h>
44#include <sys/modctl.h>
45#include <sys/vgareg.h>
46#include <sys/vgasubr.h>
47#include <sys/pci.h>
48#include <sys/kd.h>
49#include <sys/ddi_impldefs.h>
50#include <sys/sunldi.h>
51#include <sys/gfx_private.h>
52
53#define	MYNAME	"vgatext"
54
55/*
56 * Each instance of this driver has 2 minor nodes:
57 * 0: for common graphics operations
58 * 1: for agpmaster operations
59 */
60#define	GFX_MINOR		0
61#define	AGPMASTER_MINOR		1
62
63#define	MY_NBITSMINOR		1
64#define	DEV2INST(dev)		(getminor(dev) >> MY_NBITSMINOR)
65#define	DEV2MINOR(dev)		(getminor(dev) & ((1 << MY_NBITSMINOR) - 1))
66#define	INST2NODE1(inst)	(((inst) << MY_NBITSMINOR) + GFX_MINOR)
67#define	INST2NODE2(inst)	(((inst) << MY_NBITSMINOR) + AGPMASTER_MINOR)
68
69/*
70 * This variable allows for this driver to suspend even if it
71 * shouldn't.  Note that by setting it, the framebuffer will probably
72 * not come back.  So use it with a serial console, or with serial
73 * line debugging (say, for example, if this driver is being modified
74 * to support _some_ hardware doing suspend and resume).
75 */
76int vgatext_force_suspend = 0;
77
78static int vgatext_open(dev_t *, int, int, cred_t *);
79static int vgatext_close(dev_t, int, int, cred_t *);
80static int vgatext_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
81static int vgatext_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
82			    size_t *, uint_t);
83
84static struct cb_ops cb_vgatext_ops = {
85	vgatext_open,		/* cb_open */
86	vgatext_close,		/* cb_close */
87	nodev,			/* cb_strategy */
88	nodev,			/* cb_print */
89	nodev,			/* cb_dump */
90	nodev,			/* cb_read */
91	nodev,			/* cb_write */
92	vgatext_ioctl,		/* cb_ioctl */
93	vgatext_devmap,		/* cb_devmap */
94	nodev,			/* cb_mmap */
95	ddi_devmap_segmap,	/* cb_segmap */
96	nochpoll,		/* cb_chpoll */
97	ddi_prop_op,		/* cb_prop_op */
98	0,			/* cb_stream */
99	D_NEW | D_MTSAFE	/* cb_flag */
100};
101
102static int vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
103		void **result);
104static int vgatext_attach(dev_info_t *, ddi_attach_cmd_t);
105static int vgatext_detach(dev_info_t *, ddi_detach_cmd_t);
106
107static struct dev_ops vgatext_ops = {
108	DEVO_REV,		/* devo_rev */
109	0,			/* devo_refcnt */
110	vgatext_info,		/* devo_getinfo */
111	nulldev,		/* devo_identify */
112	nulldev,		/* devo_probe */
113	vgatext_attach,		/* devo_attach */
114	vgatext_detach,		/* devo_detach */
115	nodev,			/* devo_reset */
116	&cb_vgatext_ops,	/* devo_cb_ops */
117	(struct bus_ops *)NULL,	/* devo_bus_ops */
118	NULL,			/* power */
119	ddi_quiesce_not_needed,	/* quiesce */
120};
121
122struct vgatext_softc {
123	gfxp_fb_softc_ptr_t gfxp_state;
124	dev_info_t		*devi;
125};
126
127static void	*vgatext_softc_head;
128
129/* Loadable Driver stuff */
130
131static struct modldrv modldrv = {
132	&mod_driverops,		/* Type of module.  This one is a driver */
133	"VGA text driver",	/* Name of the module. */
134	&vgatext_ops,		/* driver ops */
135};
136
137static struct modlinkage modlinkage = {
138	MODREV_1, (void *) &modldrv, NULL
139};
140
141int
142_init(void)
143{
144	int e;
145
146	if ((e = ddi_soft_state_init(&vgatext_softc_head,
147		    sizeof (struct vgatext_softc), 1)) != 0) {
148	    return (e);
149	}
150
151	e = mod_install(&modlinkage);
152
153	if (e) {
154		ddi_soft_state_fini(&vgatext_softc_head);
155	}
156	return (e);
157}
158
159int
160_fini(void)
161{
162	int e;
163
164	if ((e = mod_remove(&modlinkage)) != 0)
165		return (e);
166
167	ddi_soft_state_fini(&vgatext_softc_head);
168
169	return (0);
170}
171
172int
173_info(struct modinfo *modinfop)
174{
175	return (mod_info(&modlinkage, modinfop));
176}
177
178/*
179 * handy macros
180 */
181
182#define	getsoftc(instance) ((struct vgatext_softc *)	\
183			ddi_get_soft_state(vgatext_softc_head, (instance)))
184
185#define	STREQ(a, b)	(strcmp((a), (b)) == 0)
186
187static int
188vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
189{
190	struct vgatext_softc *softc;
191	int	unit = ddi_get_instance(devi);
192	int	error;
193	char	name[80];
194
195
196	switch (cmd) {
197	case DDI_ATTACH:
198		break;
199
200	case DDI_RESUME:
201		/*
202		 * Though vgatext doesn't really know how to resume
203		 * on a generic framebuffer, we should succeed, as
204		 * it is far better to have no console, than potentiall
205		 * have no machine.
206		 */
207		softc = getsoftc(unit);
208		return (gfxp_fb_attach(devi, cmd, softc->gfxp_state));
209	default:
210		return (DDI_FAILURE);
211	}
212
213	/* DDI_ATTACH */
214
215	/* Allocate softc struct */
216	if (ddi_soft_state_zalloc(vgatext_softc_head, unit) != DDI_SUCCESS) {
217		return (DDI_FAILURE);
218	}
219	softc = getsoftc(unit);
220	softc->gfxp_state = gfxp_fb_softc_alloc();
221	if (softc->gfxp_state == NULL) {
222		(void) ddi_soft_state_free(vgatext_softc_head, unit);
223		return (DDI_FAILURE);
224	}
225
226	if (gfxp_fb_attach(devi, cmd, softc->gfxp_state) != DDI_SUCCESS) {
227		gfxp_fb_softc_free(softc->gfxp_state);
228		(void) ddi_soft_state_free(vgatext_softc_head, unit);
229		return (DDI_FAILURE);
230	}
231
232	/* link it in */
233	softc->devi = devi;
234	ddi_set_driver_private(devi, softc);
235
236	(void) snprintf(name, sizeof (name), "text-%d", unit);
237	error = ddi_create_minor_node(devi, name, S_IFCHR,
238	    INST2NODE1(unit), DDI_NT_DISPLAY, 0);
239	if (error == DDI_SUCCESS)
240		return (DDI_SUCCESS);
241
242	(void) vgatext_detach(devi, DDI_DETACH);
243	return (error);
244}
245
246static int
247vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
248{
249	int instance = ddi_get_instance(devi);
250	struct vgatext_softc *softc = getsoftc(instance);
251
252
253	switch (cmd) {
254	case DDI_DETACH:
255		(void) gfxp_fb_detach(devi, cmd, softc->gfxp_state);
256
257		if (softc->gfxp_state != NULL)
258			gfxp_fb_softc_free(softc->gfxp_state);
259		ddi_remove_minor_node(devi, NULL);
260		(void) ddi_soft_state_free(vgatext_softc_head, instance);
261		return (DDI_SUCCESS);
262
263	case DDI_SUSPEND:
264		/*
265		 * This is a generic VGA file, and therefore, cannot
266		 * understand how to deal with suspend and resume on
267		 * a generic interface.  So we fail any attempt to
268		 * suspend.  At some point in the future, we might use
269		 * this as an entrypoint for display drivers and this
270		 * assumption may change.
271		 *
272		 * However, from a platform development perspective,
273		 * it is important that this driver suspend if a
274		 * developer is using a serial console and/or working
275		 * on a framebuffer driver that will support suspend
276		 * and resume.  Therefore, we have this module tunable
277		 * (purposely using a long name) that will allow for
278		 * suspend it it is set.  Otherwise we fail.
279		 */
280		if (vgatext_force_suspend != 0)
281			return (gfxp_fb_detach(devi, cmd, softc->gfxp_state));
282		else
283			return (DDI_FAILURE);
284
285	default:
286		cmn_err(CE_WARN, "vgatext_detach: unknown cmd 0x%x\n", cmd);
287		return (DDI_FAILURE);
288	}
289}
290
291/*ARGSUSED*/
292static int
293vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
294{
295	dev_t dev;
296	int error;
297	int instance;
298	struct vgatext_softc *softc;
299
300	error = DDI_SUCCESS;
301
302	dev = (dev_t)arg;
303	instance = DEV2INST(dev);
304	softc = getsoftc(instance);
305
306	switch (infocmd) {
307	case DDI_INFO_DEVT2DEVINFO:
308		if (softc == NULL || softc->devi == NULL) {
309			error = DDI_FAILURE;
310		} else {
311			*result = (void *) softc->devi;
312			error = DDI_SUCCESS;
313		}
314		break;
315	case DDI_INFO_DEVT2INSTANCE:
316		*result = (void *)(uintptr_t)instance;
317		error = DDI_SUCCESS;
318		break;
319	default:
320		error = DDI_FAILURE;
321		break;
322	}
323	return (error);
324}
325
326
327static int
328vgatext_open(dev_t *devp, int flag, int otyp, cred_t *cred)
329{
330	struct vgatext_softc *softc = getsoftc(DEV2INST(*devp));
331
332	if (softc == NULL)
333		return (ENXIO);
334
335	return (gfxp_fb_open(devp, flag, otyp, cred, softc->gfxp_state));
336}
337
338static int
339vgatext_close(dev_t devp, int flag, int otyp, cred_t *cred)
340{
341	struct vgatext_softc *softc = getsoftc(DEV2INST(devp));
342
343	if (softc == NULL)
344		return (ENXIO);
345
346	return (gfxp_fb_close(devp, flag, otyp, cred, softc->gfxp_state));
347}
348
349static int
350vgatext_ioctl(
351    dev_t dev,
352    int cmd,
353    intptr_t data,
354    int mode,
355    cred_t *cred,
356    int *rval)
357{
358	struct vgatext_softc *softc = getsoftc(DEV2INST(dev));
359	int err;
360
361	switch (DEV2MINOR(dev)) {
362	case GFX_MINOR:
363		err = gfxp_fb_ioctl(dev, cmd, data, mode, cred, rval,
364		    softc->gfxp_state);
365		break;
366
367	case AGPMASTER_MINOR:
368		/*
369		 * This is apparently not used anymore.  Let's log a
370		 * message so we'll know if some consumer shows up.
371		 * If it turns out that we actually do need to keep
372		 * support for this pass-through to agpmaster, it
373		 * would probably be better to use "layered" access
374		 * to the AGP device (ldi_open, ldi_ioctl, ldi_close)
375		 */
376		cmn_err(CE_NOTE, "!vgatext wants agpmaster");
377		return (EBADF);
378
379	default:
380		/* not a valid minor node */
381		return (EBADF);
382	}
383	return (err);
384}
385
386static int
387vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
388    size_t *maplen, uint_t model)
389{
390	struct vgatext_softc *softc;
391
392	softc = getsoftc(DEV2INST(dev));
393	if (softc == NULL) {
394		cmn_err(CE_WARN, "vgatext: Can't find softstate");
395		return (-1);
396	}
397
398	return (gfxp_fb_devmap(dev, dhp, off, len, maplen, model,
399	    softc->gfxp_state));
400}
401