22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
23 * SUCH DAMAGE. |
23 * SUCH DAMAGE. |
24 */ |
24 */ |
25 |
25 |
26 /* |
26 /* |
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
27 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. |
28 * Use is subject to license terms. |
28 * Use is subject to license terms. |
29 */ |
29 */ |
30 |
30 |
31 /* |
31 /* |
32 * Copyright (C) 4Front Technologies 1996-2008. |
32 * Copyright (C) 4Front Technologies 1996-2008. |
99 }; |
99 }; |
100 |
100 |
101 |
101 |
102 /* |
102 /* |
103 * For the sake of simplicity, this driver fixes a few parameters with |
103 * For the sake of simplicity, this driver fixes a few parameters with |
104 * constants. If you want these values to be tunable, upgrade to a |
104 * constants. |
105 * nicer and newer device. This is all tuned for 100 Hz (10 |
|
106 * millisecs) latency. |
|
107 */ |
105 */ |
108 #define SOLO_RATE 48000 |
106 #define SOLO_RATE 48000 |
109 #define SOLO_INTRS 100 |
107 #define SOLO_FRAGFR 1024 |
110 #define SOLO_FRAGFR (SOLO_RATE / SOLO_INTRS) |
108 #define SOLO_NFRAGS 2 |
111 #define SOLO_NFRAGS 8 |
|
112 #define SOLO_NCHAN 2 |
109 #define SOLO_NCHAN 2 |
113 #define SOLO_SAMPSZ 2 |
110 #define SOLO_SAMPSZ 2 |
114 #define SOLO_FRAGSZ (SOLO_FRAGFR * (SOLO_NCHAN * SOLO_SAMPSZ)) |
111 #define SOLO_FRAGSZ (SOLO_FRAGFR * (SOLO_NCHAN * SOLO_SAMPSZ)) |
115 #define SOLO_BUFFR (SOLO_NFRAGS * SOLO_FRAGFR) |
112 #define SOLO_BUFFR (SOLO_NFRAGS * SOLO_FRAGFR) |
116 #define SOLO_BUFSZ (SOLO_NFRAGS * SOLO_FRAGSZ) |
113 #define SOLO_BUFSZ (SOLO_NFRAGS * SOLO_FRAGSZ) |
338 |
336 |
339 static uint_t |
337 static uint_t |
340 solo_intr(caddr_t arg1, caddr_t arg2) |
338 solo_intr(caddr_t arg1, caddr_t arg2) |
341 { |
339 { |
342 solo_dev_t *dev = (void *)arg1; |
340 solo_dev_t *dev = (void *)arg1; |
343 audio_engine_t *prod = NULL; |
|
344 audio_engine_t *cons = NULL; |
|
345 uint8_t status; |
341 uint8_t status; |
346 uint_t rv = DDI_INTR_UNCLAIMED; |
342 uint_t rv = DDI_INTR_UNCLAIMED; |
347 |
343 |
348 _NOTE(ARGUNUSED(arg2)); |
344 _NOTE(ARGUNUSED(arg2)); |
349 |
345 |
355 } |
351 } |
356 |
352 |
357 status = PORT_RD8(dev->io, 0x7); |
353 status = PORT_RD8(dev->io, 0x7); |
358 if (status & 0x20) { |
354 if (status & 0x20) { |
359 rv = DDI_INTR_CLAIMED; |
355 rv = DDI_INTR_CLAIMED; |
360 cons = dev->play.engine; |
|
361 /* ack the interrupt */ |
356 /* ack the interrupt */ |
362 solo_setmixer(dev, 0x7a, solo_getmixer(dev, 0x7a) & ~0x80); |
357 solo_setmixer(dev, 0x7a, solo_getmixer(dev, 0x7a) & ~0x80); |
363 } |
358 } |
364 |
359 |
365 if (status & 0x10) { |
360 if (status & 0x10) { |
366 rv = DDI_INTR_CLAIMED; |
361 rv = DDI_INTR_CLAIMED; |
367 prod = dev->rec.engine; |
|
368 /* ack the interrupt */ |
362 /* ack the interrupt */ |
369 (void) PORT_RD8(dev->sb, 0xe); |
363 (void) PORT_RD8(dev->sb, 0xe); |
370 } |
364 } |
371 mutex_exit(&dev->mutex); |
365 mutex_exit(&dev->mutex); |
372 |
|
373 if (cons) { |
|
374 audio_engine_consume(cons); |
|
375 } |
|
376 |
|
377 if (prod) { |
|
378 audio_engine_produce(prod); |
|
379 } |
|
380 |
366 |
381 return (rv); |
367 return (rv); |
382 } |
368 } |
383 |
369 |
384 static uint8_t |
370 static uint8_t |
749 solo_dev_t *dev = e->dev; |
731 solo_dev_t *dev = e->dev; |
750 uint16_t offset, n; |
732 uint16_t offset, n; |
751 uint32_t ptr; |
733 uint32_t ptr; |
752 uint32_t count; |
734 uint32_t count; |
753 uint32_t diff; |
735 uint32_t diff; |
|
736 int tries; |
754 |
737 |
755 ASSERT(mutex_owned(&dev->mutex)); |
738 ASSERT(mutex_owned(&dev->mutex)); |
756 |
739 |
757 /* |
740 /* |
758 * During recording, this register is known to give back |
741 * During recording, this register is known to give back |
759 * garbage if it's not quiescent while being read. This hack |
742 * garbage if it's not quiescent while being read. This hack |
760 * attempts to work around it. |
743 * attempts to work around it. We also suspend the DMA |
|
744 * while we do this, to minimize record distortion. |
761 */ |
745 */ |
762 ptr = PORT_RD32(dev->vc, 0); |
746 if (e->trigger) { |
763 count = PORT_RD16(dev->vc, 4); |
747 drv_usecwait(20); |
764 diff = e->paddr + SOLO_BUFSZ - ptr - count; |
748 } |
765 if ((diff > 3) || (ptr < e->paddr) || |
749 for (tries = 10; tries; tries--) { |
766 (ptr >= (e->paddr + SOLO_BUFSZ))) { |
750 drv_usecwait(10); |
767 ptr = dev->last_capture; |
751 ptr = PORT_RD32(dev->vc, 0); |
768 } else { |
752 count = PORT_RD16(dev->vc, 4); |
769 dev->last_capture = ptr; |
753 diff = e->paddr + SOLO_BUFSZ - ptr - count; |
770 } |
754 if ((diff > 3) || (ptr < e->paddr) || |
|
755 (ptr >= (e->paddr + SOLO_BUFSZ))) { |
|
756 ptr = dev->last_capture; |
|
757 } else { |
|
758 break; |
|
759 } |
|
760 } |
|
761 if (e->trigger) { |
|
762 PORT_WR8(dev->vc, 0xf, 0); /* restart DMA */ |
|
763 } |
|
764 if (!tries) { |
|
765 /* |
|
766 * Note, this is a pretty bad situation, because we'll |
|
767 * not have an accurate idea of our position. But its |
|
768 * better than making a bad alteration. If we had FMA |
|
769 * for audio devices, this would be a good point to |
|
770 * raise a fault. |
|
771 */ |
|
772 return; |
|
773 } |
|
774 dev->last_capture = ptr; |
771 offset = ptr - e->paddr; |
775 offset = ptr - e->paddr; |
772 offset /= (SOLO_NCHAN * SOLO_SAMPSZ); |
776 offset /= (SOLO_NCHAN * SOLO_SAMPSZ); |
773 |
777 |
774 n = offset >= e->offset ? |
778 n = offset >= e->offset ? |
775 offset - e->offset : |
779 offset - e->offset : |
829 /* autoinit, dma dir, go for it */ |
833 /* autoinit, dma dir, go for it */ |
830 solo_write(dev, 0xb8, 0x0f); |
834 solo_write(dev, 0xb8, 0x0f); |
831 PORT_WR8(dev->vc, 0xf, 0); /* start DMA */ |
835 PORT_WR8(dev->vc, 0xf, 0); /* start DMA */ |
832 |
836 |
833 dev->last_capture = e->paddr; |
837 dev->last_capture = e->paddr; |
|
838 e->trigger = true; |
834 } |
839 } |
835 |
840 |
836 static void |
841 static void |
837 solo_aud1_stop(solo_engine_t *e) |
842 solo_aud1_stop(solo_engine_t *e) |
838 { |
843 { |
839 solo_dev_t *dev = e->dev; |
844 solo_dev_t *dev = e->dev; |
840 |
845 |
841 /* NB: We might be in quiesce, without a lock held */ |
846 /* NB: We might be in quiesce, without a lock held */ |
842 solo_write(dev, 0xb8, solo_read(dev, 0xb8) & ~0x01); |
847 solo_write(dev, 0xb8, solo_read(dev, 0xb8) & ~0x01); |
|
848 e->trigger = false; |
843 } |
849 } |
844 |
850 |
845 static void |
851 static void |
846 solo_aud2_update(solo_engine_t *e) |
852 solo_aud2_update(solo_engine_t *e) |
847 { |
853 { |
898 PORT_WR8(dev->io, 0x6, 0x0a); /* autoinit, enable */ |
904 PORT_WR8(dev->io, 0x6, 0x0a); /* autoinit, enable */ |
899 |
905 |
900 v = solo_mixer_scale(dev, CTL_VOLUME); |
906 v = solo_mixer_scale(dev, CTL_VOLUME); |
901 v = v | (v << 4); |
907 v = v | (v << 4); |
902 solo_setmixer(dev, 0x7c, v & 0xff); |
908 solo_setmixer(dev, 0x7c, v & 0xff); |
|
909 |
|
910 e->trigger = true; |
903 } |
911 } |
904 |
912 |
905 static void |
913 static void |
906 solo_aud2_stop(solo_engine_t *e) |
914 solo_aud2_stop(solo_engine_t *e) |
907 { |
915 { |
908 solo_dev_t *dev = e->dev; |
916 solo_dev_t *dev = e->dev; |
909 |
917 |
910 /* NB: We might be in quiesce, without a lock held */ |
918 /* NB: We might be in quiesce, without a lock held */ |
911 PORT_WR8(dev->io, 0x6, 0); |
919 PORT_WR8(dev->io, 0x6, 0); |
912 solo_setmixer(dev, 0x78, solo_getmixer(dev, 0x78) & ~0x03); |
920 solo_setmixer(dev, 0x78, solo_getmixer(dev, 0x78) & ~0x03); |
|
921 |
|
922 e->trigger = false; |
913 } |
923 } |
914 |
924 |
915 /* |
925 /* |
916 * Audio entry points. |
926 * Audio entry points. |
917 */ |
927 */ |
966 solo_engine_t *e = arg; |
976 solo_engine_t *e = arg; |
967 solo_dev_t *dev = e->dev; |
977 solo_dev_t *dev = e->dev; |
968 uint64_t count; |
978 uint64_t count; |
969 |
979 |
970 mutex_enter(&dev->mutex); |
980 mutex_enter(&dev->mutex); |
971 if (!dev->suspended) |
981 e->update(e); |
972 e->update(e); |
|
973 count = e->count; |
982 count = e->count; |
974 mutex_exit(&dev->mutex); |
983 mutex_exit(&dev->mutex); |
975 |
984 |
976 return (count); |
985 return (count); |
977 } |
986 } |
978 |
987 |
979 static int |
988 static int |
980 solo_open(void *arg, int f, unsigned *ffr, unsigned *nfr, caddr_t *buf) |
989 solo_open(void *arg, int f, unsigned *nframes, caddr_t *buf) |
981 { |
990 { |
982 solo_engine_t *e = arg; |
991 solo_engine_t *e = arg; |
983 solo_dev_t *dev = e->dev; |
992 solo_dev_t *dev = e->dev; |
984 |
993 |
985 _NOTE(ARGUNUSED(f)); |
994 _NOTE(ARGUNUSED(f)); |
986 |
995 |
987 /* NB: For simplicity, we just fix the interrupt rate at 100 Hz */ |
996 *nframes = SOLO_NFRAGS * SOLO_FRAGFR; |
988 *ffr = SOLO_FRAGFR; |
|
989 *nfr = SOLO_NFRAGS; |
|
990 *buf = e->kaddr; |
997 *buf = e->kaddr; |
991 |
998 |
992 mutex_enter(&dev->mutex); |
999 mutex_enter(&dev->mutex); |
993 e->started = false; |
1000 e->started = false; |
994 e->count = 0; |
1001 e->count = 0; |
1330 |
1334 |
1331 |
1335 |
1332 static int |
1336 static int |
1333 solo_suspend(solo_dev_t *dev) |
1337 solo_suspend(solo_dev_t *dev) |
1334 { |
1338 { |
|
1339 audio_dev_suspend(dev->adev); |
|
1340 |
1335 mutex_enter(&dev->mutex); |
1341 mutex_enter(&dev->mutex); |
1336 /* play */ |
|
1337 solo_aud2_stop(&dev->play); |
|
1338 solo_aud2_update(&dev->play); |
|
1339 /* record */ |
|
1340 solo_aud1_stop(&dev->rec); |
|
1341 solo_aud1_update(&dev->rec); |
|
1342 |
|
1343 dev->suspended = true; |
1342 dev->suspended = true; |
1344 mutex_exit(&dev->mutex); |
1343 mutex_exit(&dev->mutex); |
1345 |
1344 |
1346 return (DDI_SUCCESS); |
1345 return (DDI_SUCCESS); |
1347 } |
1346 } |
1348 |
1347 |
1349 static int |
1348 static int |
1350 solo_resume(solo_dev_t *dev) |
1349 solo_resume(solo_dev_t *dev) |
1351 { |
1350 { |
1352 solo_engine_t *e; |
|
1353 audio_engine_t *prod = NULL; |
|
1354 audio_engine_t *cons = NULL; |
|
1355 |
|
1356 audio_engine_reset(dev->rec.engine); |
|
1357 audio_engine_reset(dev->play.engine); |
|
1358 |
|
1359 mutex_enter(&dev->mutex); |
1351 mutex_enter(&dev->mutex); |
1360 if (!solo_init_hw(dev)) { |
1352 if (!solo_init_hw(dev)) { |
1361 /* yikes! */ |
1353 /* yikes! */ |
1362 audio_dev_warn(dev->adev, "unable to resume audio!"); |
1354 audio_dev_warn(dev->adev, "unable to resume audio!"); |
1363 audio_dev_warn(dev->adev, "reboot or reload driver to reset"); |
1355 audio_dev_warn(dev->adev, "reboot or reload driver to reset"); |
1364 mutex_exit(&dev->mutex); |
|
1365 return (DDI_SUCCESS); |
|
1366 } |
1356 } |
1367 dev->suspended = false; |
1357 dev->suspended = false; |
1368 |
|
1369 /* record - audio 1 */ |
|
1370 e = &dev->rec; |
|
1371 if (e->started) { |
|
1372 e->start(e); |
|
1373 prod = e->engine; |
|
1374 } |
|
1375 |
|
1376 /* play - audio 2 */ |
|
1377 e = &dev->play; |
|
1378 if (e->started) { |
|
1379 e->start(e); |
|
1380 cons = e->engine; |
|
1381 } |
|
1382 |
|
1383 mutex_exit(&dev->mutex); |
1358 mutex_exit(&dev->mutex); |
1384 |
1359 |
1385 if (cons) |
1360 audio_dev_resume(dev->adev); |
1386 audio_engine_consume(cons); |
|
1387 if (prod) |
|
1388 audio_engine_produce(prod); |
|
1389 |
1361 |
1390 return (DDI_SUCCESS); |
1362 return (DDI_SUCCESS); |
1391 } |
1363 } |
1392 |
1364 |
1393 static int |
1365 static int |