10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 23*946Smathue * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/stat.h> 300Sstevel@tonic-gate #include <sys/file.h> 310Sstevel@tonic-gate #include <sys/uio.h> 320Sstevel@tonic-gate #include <sys/modctl.h> 330Sstevel@tonic-gate #include <sys/open.h> 340Sstevel@tonic-gate #include <sys/types.h> 350Sstevel@tonic-gate #include <sys/kmem.h> 360Sstevel@tonic-gate #include <sys/systm.h> 370Sstevel@tonic-gate #include <sys/ddi.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate #include <sys/conf.h> 400Sstevel@tonic-gate #include <sys/mode.h> 410Sstevel@tonic-gate #include <sys/note.h> 420Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h> 430Sstevel@tonic-gate #include <sys/i2c/clients/tda8444_impl.h> 440Sstevel@tonic-gate 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * cb ops 470Sstevel@tonic-gate */ 480Sstevel@tonic-gate static int tda8444_open(dev_t *, int, int, cred_t *); 490Sstevel@tonic-gate static int tda8444_close(dev_t, int, int, cred_t *); 500Sstevel@tonic-gate static int tda8444_read(dev_t dev, struct uio *uiop, cred_t *cred_p); 510Sstevel@tonic-gate static int tda8444_write(dev_t dev, struct uio *uiop, cred_t *cred_p); 520Sstevel@tonic-gate static int tda8444_io(dev_t dev, struct uio *uiop, int rw); 530Sstevel@tonic-gate /* 540Sstevel@tonic-gate * dev ops 550Sstevel@tonic-gate */ 560Sstevel@tonic-gate static int tda8444_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 570Sstevel@tonic-gate void **result); 580Sstevel@tonic-gate static int tda8444_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 590Sstevel@tonic-gate static int tda8444_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 600Sstevel@tonic-gate 610Sstevel@tonic-gate static struct cb_ops tda8444_cbops = { 620Sstevel@tonic-gate tda8444_open, /* open */ 630Sstevel@tonic-gate tda8444_close, /* close */ 640Sstevel@tonic-gate nodev, /* strategy */ 650Sstevel@tonic-gate nodev, /* print */ 660Sstevel@tonic-gate nodev, /* dump */ 670Sstevel@tonic-gate tda8444_read, /* read */ 680Sstevel@tonic-gate tda8444_write, /* write */ 690Sstevel@tonic-gate nodev, /* ioctl */ 700Sstevel@tonic-gate nodev, /* devmap */ 710Sstevel@tonic-gate nodev, /* mmap */ 720Sstevel@tonic-gate nodev, /* segmap */ 730Sstevel@tonic-gate nochpoll, /* poll */ 740Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 750Sstevel@tonic-gate NULL, /* streamtab */ 760Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 770Sstevel@tonic-gate CB_REV, /* rev */ 780Sstevel@tonic-gate nodev, /* int (*cb_aread)() */ 790Sstevel@tonic-gate nodev /* int (*cb_awrite)() */ 800Sstevel@tonic-gate }; 810Sstevel@tonic-gate 820Sstevel@tonic-gate static struct dev_ops tda8444_ops = { 830Sstevel@tonic-gate DEVO_REV, 840Sstevel@tonic-gate 0, 850Sstevel@tonic-gate tda8444_info, 860Sstevel@tonic-gate nulldev, 870Sstevel@tonic-gate nulldev, 880Sstevel@tonic-gate tda8444_attach, 890Sstevel@tonic-gate tda8444_detach, 900Sstevel@tonic-gate nodev, 910Sstevel@tonic-gate &tda8444_cbops, 920Sstevel@tonic-gate NULL 930Sstevel@tonic-gate }; 940Sstevel@tonic-gate 950Sstevel@tonic-gate static struct modldrv tda8444_modldrv = { 960Sstevel@tonic-gate &mod_driverops, /* type of module - driver */ 970Sstevel@tonic-gate "tda8444 device driver v%I%", 980Sstevel@tonic-gate &tda8444_ops, 990Sstevel@tonic-gate }; 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate static struct modlinkage tda8444_modlinkage = { 1020Sstevel@tonic-gate MODREV_1, 1030Sstevel@tonic-gate &tda8444_modldrv, 1040Sstevel@tonic-gate 0 1050Sstevel@tonic-gate }; 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate static void *tda8444_soft_statep; 1080Sstevel@tonic-gate static int tda8444_debug = 0; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate int 1110Sstevel@tonic-gate _init(void) 1120Sstevel@tonic-gate { 1130Sstevel@tonic-gate int error; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate error = mod_install(&tda8444_modlinkage); 1160Sstevel@tonic-gate if (error == 0) { 1170Sstevel@tonic-gate (void) ddi_soft_state_init(&tda8444_soft_statep, 1180Sstevel@tonic-gate sizeof (struct tda8444_unit), TDA8444_MAX_DACS); 1190Sstevel@tonic-gate } 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate return (error); 1220Sstevel@tonic-gate } 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate int 1250Sstevel@tonic-gate _fini(void) 1260Sstevel@tonic-gate { 1270Sstevel@tonic-gate int error; 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate error = mod_remove(&tda8444_modlinkage); 1300Sstevel@tonic-gate if (error == 0) { 1310Sstevel@tonic-gate ddi_soft_state_fini(&tda8444_soft_statep); 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate return (error); 1350Sstevel@tonic-gate } 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate int 1380Sstevel@tonic-gate _info(struct modinfo *modinfop) 1390Sstevel@tonic-gate { 1400Sstevel@tonic-gate return (mod_info(&tda8444_modlinkage, modinfop)); 1410Sstevel@tonic-gate } 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate /* ARGSUSED */ 1440Sstevel@tonic-gate static int 1450Sstevel@tonic-gate tda8444_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 1460Sstevel@tonic-gate { 1470Sstevel@tonic-gate dev_t dev; 1480Sstevel@tonic-gate int instance; 1490Sstevel@tonic-gate 1500Sstevel@tonic-gate if (infocmd == DDI_INFO_DEVT2INSTANCE) { 1510Sstevel@tonic-gate dev = (dev_t)arg; 1520Sstevel@tonic-gate instance = TDA8444_MINOR_TO_DEVINST(dev); 1530Sstevel@tonic-gate *result = (void *)(uintptr_t)instance; 1540Sstevel@tonic-gate return (DDI_SUCCESS); 1550Sstevel@tonic-gate } 1560Sstevel@tonic-gate return (DDI_FAILURE); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate static int 1600Sstevel@tonic-gate tda8444_do_resume(dev_info_t *dip) 1610Sstevel@tonic-gate { 1620Sstevel@tonic-gate int instance = ddi_get_instance(dip); 1630Sstevel@tonic-gate struct tda8444_unit *unitp; 1640Sstevel@tonic-gate int channel; 1650Sstevel@tonic-gate int ret = DDI_SUCCESS; 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate unitp = (struct tda8444_unit *) 1680Sstevel@tonic-gate ddi_get_soft_state(tda8444_soft_statep, instance); 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate if (unitp == NULL) { 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate return (ENXIO); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate for (channel = 0; channel < TDA8444_CHANS; channel++) { 1760Sstevel@tonic-gate unitp->tda8444_transfer->i2c_wbuf[0] = TDA8444_REGBASE | 1770Sstevel@tonic-gate channel; 1780Sstevel@tonic-gate unitp->tda8444_transfer->i2c_wbuf[1] = 1790Sstevel@tonic-gate unitp->tda8444_output[channel]; 1800Sstevel@tonic-gate DPRINTF(RESUME, ("tda8444_resume: setting channel %d to %d", 1810Sstevel@tonic-gate channel, unitp->tda8444_output[channel])); 1820Sstevel@tonic-gate if (i2c_transfer(unitp->tda8444_hdl, 1830Sstevel@tonic-gate unitp->tda8444_transfer) != I2C_SUCCESS) { 1840Sstevel@tonic-gate ret = DDI_FAILURE; 1850Sstevel@tonic-gate } 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 1890Sstevel@tonic-gate unitp->tda8444_flags = 0; 1900Sstevel@tonic-gate cv_signal(&unitp->tda8444_cv); 1910Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate return (ret); 1940Sstevel@tonic-gate } 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate static int 1970Sstevel@tonic-gate tda8444_do_attach(dev_info_t *dip) 1980Sstevel@tonic-gate { 1990Sstevel@tonic-gate struct tda8444_unit *unitp; 2000Sstevel@tonic-gate char name[MAXNAMELEN]; 2010Sstevel@tonic-gate int instance; 2020Sstevel@tonic-gate minor_t minor; 2030Sstevel@tonic-gate int i; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate instance = ddi_get_instance(dip); 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate if (ddi_soft_state_zalloc(tda8444_soft_statep, instance) != 0) { 2080Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d failed to zalloc softstate", 2090Sstevel@tonic-gate ddi_get_name(dip), instance); 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate return (DDI_FAILURE); 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate if (unitp == NULL) { 2170Sstevel@tonic-gate return (DDI_FAILURE); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate (void) snprintf(unitp->tda8444_name, sizeof (unitp->tda8444_name), 2210Sstevel@tonic-gate "%s%d", ddi_driver_name(dip), instance); 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate for (i = 0; i < TDA8444_CHANS; i++) { 2240Sstevel@tonic-gate (void) sprintf(name, "%d", i); 2250Sstevel@tonic-gate minor = TDA8444_CHANNEL_TO_MINOR(i) | 2260Sstevel@tonic-gate TDA8444_DEVINST_TO_MINOR(instance); 2270Sstevel@tonic-gate if (ddi_create_minor_node(dip, name, S_IFCHR, minor, 2280Sstevel@tonic-gate TDA8444_NODE_TYPE, NULL) == DDI_FAILURE) { 2290Sstevel@tonic-gate cmn_err(CE_WARN, "%s ddi_create_minor_node failed", 2300Sstevel@tonic-gate unitp->tda8444_name); 2310Sstevel@tonic-gate ddi_soft_state_free(tda8444_soft_statep, instance); 2320Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate return (DDI_FAILURE); 2350Sstevel@tonic-gate } 2360Sstevel@tonic-gate unitp->tda8444_output[i] = TDA8444_UNKNOWN_OUT; 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate /* 2400Sstevel@tonic-gate * preallocate a single buffer for all writes 2410Sstevel@tonic-gate */ 2420Sstevel@tonic-gate if (i2c_transfer_alloc(unitp->tda8444_hdl, &unitp->tda8444_transfer, 2430Sstevel@tonic-gate 2, 0, I2C_SLEEP) != I2C_SUCCESS) { 2440Sstevel@tonic-gate cmn_err(CE_WARN, "i2c_transfer_alloc failed"); 2450Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2460Sstevel@tonic-gate ddi_soft_state_free(tda8444_soft_statep, instance); 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate return (DDI_FAILURE); 2490Sstevel@tonic-gate } 2500Sstevel@tonic-gate unitp->tda8444_transfer->i2c_flags = I2C_WR; 2510Sstevel@tonic-gate unitp->tda8444_transfer->i2c_version = I2C_XFER_REV; 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate if (i2c_client_register(dip, &unitp->tda8444_hdl) != I2C_SUCCESS) { 2540Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2550Sstevel@tonic-gate cmn_err(CE_WARN, "i2c_client_register failed"); 2560Sstevel@tonic-gate ddi_soft_state_free(tda8444_soft_statep, instance); 2570Sstevel@tonic-gate i2c_transfer_free(unitp->tda8444_hdl, unitp->tda8444_transfer); 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate return (DDI_FAILURE); 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate mutex_init(&unitp->tda8444_mutex, NULL, MUTEX_DRIVER, NULL); 2630Sstevel@tonic-gate cv_init(&unitp->tda8444_cv, NULL, CV_DRIVER, NULL); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate return (DDI_SUCCESS); 2660Sstevel@tonic-gate } 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate static int 2690Sstevel@tonic-gate tda8444_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2700Sstevel@tonic-gate { 2710Sstevel@tonic-gate switch (cmd) { 2720Sstevel@tonic-gate case DDI_ATTACH: 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate return (tda8444_do_attach(dip)); 2750Sstevel@tonic-gate case DDI_RESUME: 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate return (tda8444_do_resume(dip)); 2780Sstevel@tonic-gate default: 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate return (DDI_FAILURE); 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate } 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate static int 2850Sstevel@tonic-gate tda8444_do_detach(dev_info_t *dip) 2860Sstevel@tonic-gate { 2870Sstevel@tonic-gate struct tda8444_unit *unitp; 2880Sstevel@tonic-gate int instance; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate instance = ddi_get_instance(dip); 2910Sstevel@tonic-gate unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate i2c_transfer_free(unitp->tda8444_hdl, unitp->tda8444_transfer); 2940Sstevel@tonic-gate i2c_client_unregister(unitp->tda8444_hdl); 2950Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 2960Sstevel@tonic-gate mutex_destroy(&unitp->tda8444_mutex); 2970Sstevel@tonic-gate cv_destroy(&unitp->tda8444_cv); 2980Sstevel@tonic-gate ddi_soft_state_free(tda8444_soft_statep, instance); 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate return (DDI_SUCCESS); 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate static int 3040Sstevel@tonic-gate tda8444_do_suspend(dev_info_t *dip) 3050Sstevel@tonic-gate { 3060Sstevel@tonic-gate struct tda8444_unit *unitp; 3070Sstevel@tonic-gate int instance; 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate instance = ddi_get_instance(dip); 3100Sstevel@tonic-gate unitp = ddi_get_soft_state(tda8444_soft_statep, instance); 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate /* 3130Sstevel@tonic-gate * Set the busy flag so that future transactions block 3140Sstevel@tonic-gate * until resume. 3150Sstevel@tonic-gate */ 3160Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 3170Sstevel@tonic-gate while (unitp->tda8444_flags == TDA8444_BUSY) { 3180Sstevel@tonic-gate if (cv_wait_sig(&unitp->tda8444_cv, 3190Sstevel@tonic-gate &unitp->tda8444_mutex) <= 0) { 3200Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate return (DDI_FAILURE); 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate unitp->tda8444_flags = TDA8444_SUSPENDED; 3260Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 3270Sstevel@tonic-gate return (DDI_SUCCESS); 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate static int 3310Sstevel@tonic-gate tda8444_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3320Sstevel@tonic-gate { 3330Sstevel@tonic-gate switch (cmd) { 3340Sstevel@tonic-gate case DDI_DETACH: 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate return (tda8444_do_detach(dip)); 3370Sstevel@tonic-gate case DDI_SUSPEND: 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate return (tda8444_do_suspend(dip)); 3400Sstevel@tonic-gate default: 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate return (DDI_FAILURE); 3430Sstevel@tonic-gate } 3440Sstevel@tonic-gate } 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate static int 3470Sstevel@tonic-gate tda8444_open(dev_t *devp, int flags, int otyp, cred_t *credp) 3480Sstevel@tonic-gate { 3490Sstevel@tonic-gate _NOTE(ARGUNUSED(credp)) 3500Sstevel@tonic-gate struct tda8444_unit *unitp; 3510Sstevel@tonic-gate int err = 0; 3520Sstevel@tonic-gate int instance = TDA8444_MINOR_TO_DEVINST(*devp); 3530Sstevel@tonic-gate int channel = TDA8444_MINOR_TO_CHANNEL(*devp); 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate if (instance < 0) { 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate return (ENXIO); 3580Sstevel@tonic-gate } 3590Sstevel@tonic-gate 3600Sstevel@tonic-gate unitp = (struct tda8444_unit *) 3610Sstevel@tonic-gate ddi_get_soft_state(tda8444_soft_statep, instance); 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate if (unitp == NULL) { 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate return (ENXIO); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate if (otyp != OTYP_CHR) { 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate return (EINVAL); 3710Sstevel@tonic-gate } 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate if (flags & FEXCL) { 3760Sstevel@tonic-gate if (unitp->tda8444_oflag[channel] != 0) { 3770Sstevel@tonic-gate err = EBUSY; 3780Sstevel@tonic-gate } else { 3790Sstevel@tonic-gate unitp->tda8444_oflag[channel] = FEXCL; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate } else { 3820Sstevel@tonic-gate if (unitp->tda8444_oflag[channel] == FEXCL) { 3830Sstevel@tonic-gate err = EBUSY; 3840Sstevel@tonic-gate } else { 385*946Smathue unitp->tda8444_oflag[channel] = (uint16_t)FOPEN; 3860Sstevel@tonic-gate } 3870Sstevel@tonic-gate } 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate return (err); 3920Sstevel@tonic-gate } 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate static int 3950Sstevel@tonic-gate tda8444_close(dev_t dev, int flags, int otyp, cred_t *credp) 3960Sstevel@tonic-gate { 3970Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, otyp, credp)) 3980Sstevel@tonic-gate struct tda8444_unit *unitp; 3990Sstevel@tonic-gate int instance = TDA8444_MINOR_TO_DEVINST(dev); 4000Sstevel@tonic-gate int channel = TDA8444_MINOR_TO_CHANNEL(dev); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate if (instance < 0) { 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate return (ENXIO); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate unitp = (struct tda8444_unit *) 4080Sstevel@tonic-gate ddi_get_soft_state(tda8444_soft_statep, instance); 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if (unitp == NULL) { 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate return (ENXIO); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate unitp->tda8444_oflag[channel] = 0; 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate return (DDI_SUCCESS); 4220Sstevel@tonic-gate } 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate static int 4250Sstevel@tonic-gate tda8444_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 4260Sstevel@tonic-gate { 4270Sstevel@tonic-gate _NOTE(ARGUNUSED(cred_p)) 4280Sstevel@tonic-gate return (tda8444_io(dev, uiop, B_READ)); 4290Sstevel@tonic-gate } 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate static int 4320Sstevel@tonic-gate tda8444_write(dev_t dev, struct uio *uiop, cred_t *cred_p) 4330Sstevel@tonic-gate { 4340Sstevel@tonic-gate _NOTE(ARGUNUSED(cred_p)) 4350Sstevel@tonic-gate return (tda8444_io(dev, uiop, B_WRITE)); 4360Sstevel@tonic-gate } 4370Sstevel@tonic-gate 4380Sstevel@tonic-gate static int 4390Sstevel@tonic-gate tda8444_io(dev_t dev, struct uio *uiop, int rw) 4400Sstevel@tonic-gate { 4410Sstevel@tonic-gate struct tda8444_unit *unitp; 4420Sstevel@tonic-gate int instance = TDA8444_MINOR_TO_DEVINST(getminor(dev)); 4430Sstevel@tonic-gate int channel = TDA8444_MINOR_TO_CHANNEL(getminor(dev)); 4440Sstevel@tonic-gate int ret = 0; 4450Sstevel@tonic-gate size_t len = uiop->uio_resid; 4460Sstevel@tonic-gate int8_t out_value; 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate if (instance < 0) { 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate return (ENXIO); 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate if (len == 0) { 4540Sstevel@tonic-gate return (0); 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate unitp = (struct tda8444_unit *) 4580Sstevel@tonic-gate ddi_get_soft_state(tda8444_soft_statep, instance); 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate if (unitp == NULL) { 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate return (ENXIO); 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate if (rw == B_READ) { 4660Sstevel@tonic-gate if (unitp->tda8444_output[channel] != TDA8444_UNKNOWN_OUT) { 4670Sstevel@tonic-gate return (uiomove(&unitp->tda8444_output[channel], 1, 4680Sstevel@tonic-gate UIO_READ, uiop)); 4690Sstevel@tonic-gate } else { 4700Sstevel@tonic-gate return (EIO); 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate /* 4750Sstevel@tonic-gate * rw == B_WRITE. Make sure each write to a device is single 4760Sstevel@tonic-gate * threaded since we pre-allocate a single write buffer. This is not a 4770Sstevel@tonic-gate * bottleneck since concurrent writes would serialize at the 4780Sstevel@tonic-gate * transport level anyway. 4790Sstevel@tonic-gate */ 4800Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 4810Sstevel@tonic-gate if (unitp->tda8444_flags == TDA8444_SUSPENDED) { 4820Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate return (EAGAIN); 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate while (unitp->tda8444_flags == TDA8444_BUSY) { 4880Sstevel@tonic-gate if (cv_wait_sig(&unitp->tda8444_cv, 4890Sstevel@tonic-gate &unitp->tda8444_mutex) <= 0) { 4900Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate return (EINTR); 4930Sstevel@tonic-gate } 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate unitp->tda8444_flags = TDA8444_BUSY; 4960Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate unitp->tda8444_transfer->i2c_wbuf[0] = (TDA8444_REGBASE | channel); 4990Sstevel@tonic-gate if ((ret = uiomove(&out_value, sizeof (out_value), UIO_WRITE, 5000Sstevel@tonic-gate uiop)) == 0) { 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate /* 5030Sstevel@tonic-gate * Check bounds 5040Sstevel@tonic-gate */ 5050Sstevel@tonic-gate if ((out_value > TDA8444_MAX_OUT) || 5060Sstevel@tonic-gate (out_value < TDA8444_MIN_OUT)) { 5070Sstevel@tonic-gate ret = EINVAL; 5080Sstevel@tonic-gate } else { 5090Sstevel@tonic-gate unitp->tda8444_transfer->i2c_wbuf[1] = 5100Sstevel@tonic-gate (uchar_t)out_value; 5110Sstevel@tonic-gate DPRINTF(IO, ("setting channel %d to %d", channel, 5120Sstevel@tonic-gate unitp->tda8444_transfer->i2c_wbuf[1])); 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate if (i2c_transfer(unitp->tda8444_hdl, 5150Sstevel@tonic-gate unitp->tda8444_transfer) != I2C_SUCCESS) { 5160Sstevel@tonic-gate ret = EIO; 5170Sstevel@tonic-gate } else { 5180Sstevel@tonic-gate unitp->tda8444_output[channel] = out_value; 5190Sstevel@tonic-gate } 5200Sstevel@tonic-gate } 5210Sstevel@tonic-gate } else { 5220Sstevel@tonic-gate ret = EFAULT; 5230Sstevel@tonic-gate } 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate mutex_enter(&unitp->tda8444_mutex); 5260Sstevel@tonic-gate unitp->tda8444_flags = 0; 5270Sstevel@tonic-gate cv_signal(&unitp->tda8444_cv); 5280Sstevel@tonic-gate mutex_exit(&unitp->tda8444_mutex); 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate return (ret); 5310Sstevel@tonic-gate } 532