1*322f3613Sriastradh /* $NetBSD: pseye.c,v 1.29 2022/03/03 06:23:25 riastradh Exp $ */
26902ca74Sjmcneill
36902ca74Sjmcneill /*-
46902ca74Sjmcneill * Copyright (c) 2008 Jared D. McNeill <jmcneill@invisible.ca>
56902ca74Sjmcneill * All rights reserved.
66902ca74Sjmcneill *
76902ca74Sjmcneill * Redistribution and use in source and binary forms, with or without
86902ca74Sjmcneill * modification, are permitted provided that the following conditions
96902ca74Sjmcneill * are met:
106902ca74Sjmcneill * 1. Redistributions of source code must retain the above copyright
116902ca74Sjmcneill * notice, this list of conditions and the following disclaimer.
126902ca74Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
136902ca74Sjmcneill * notice, this list of conditions and the following disclaimer in the
146902ca74Sjmcneill * documentation and/or other materials provided with the distribution.
156902ca74Sjmcneill *
166902ca74Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
176902ca74Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
186902ca74Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
196902ca74Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
206902ca74Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
216902ca74Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
226902ca74Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
236902ca74Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
246902ca74Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
256902ca74Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
266902ca74Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
276902ca74Sjmcneill */
286902ca74Sjmcneill
29548abe3dSjmcneill /*
3039177e11Sjmcneill * Sony PlayStation Eye Driver
31548abe3dSjmcneill *
32548abe3dSjmcneill * The only documentation we have for this part is based on a series
33548abe3dSjmcneill * of forum postings by Jim Paris on ps2dev.org. Many thanks for
34548abe3dSjmcneill * figuring this one out.
35548abe3dSjmcneill *
36548abe3dSjmcneill * URL: http://forums.ps2dev.org/viewtopic.php?t=9238
37548abe3dSjmcneill */
38548abe3dSjmcneill
396902ca74Sjmcneill #include <sys/cdefs.h>
40*322f3613Sriastradh __KERNEL_RCSID(0, "$NetBSD: pseye.c,v 1.29 2022/03/03 06:23:25 riastradh Exp $");
416902ca74Sjmcneill
426902ca74Sjmcneill #include <sys/param.h>
436902ca74Sjmcneill #include <sys/systm.h>
446902ca74Sjmcneill #include <sys/device.h>
456902ca74Sjmcneill #include <sys/fcntl.h>
466902ca74Sjmcneill #include <sys/conf.h>
476902ca74Sjmcneill #include <sys/poll.h>
486902ca74Sjmcneill #include <sys/bus.h>
496902ca74Sjmcneill #include <sys/mutex.h>
506902ca74Sjmcneill #include <sys/kthread.h>
516902ca74Sjmcneill #include <sys/condvar.h>
525a635bb2Sjmcneill #include <sys/module.h>
536902ca74Sjmcneill
546902ca74Sjmcneill #include <dev/usb/usb.h>
556902ca74Sjmcneill #include <dev/usb/usbdi.h>
56df0c38f6Sjmcneill #include <dev/usb/usbdivar.h>
576902ca74Sjmcneill #include <dev/usb/usbdi_util.h>
586902ca74Sjmcneill #include <dev/usb/usbdevs.h>
596277e868Sjmcneill #include <dev/usb/uvideoreg.h>
606902ca74Sjmcneill
616902ca74Sjmcneill #include <dev/video_if.h>
626902ca74Sjmcneill
636ea027b8Sjmcneill #define PRI_PSEYE PRI_BIO
646ea027b8Sjmcneill
656277e868Sjmcneill /* Bulk-in buffer length -- make room for payload + UVC headers */
666277e868Sjmcneill #define PSEYE_BULKIN_BUFLEN ((640 * 480 * 2) + 4096)
676277e868Sjmcneill #define PSEYE_BULKIN_BLKLEN 2048
686902ca74Sjmcneill
696902ca74Sjmcneill /* SCCB/sensor interface */
706902ca74Sjmcneill #define PSEYE_SCCB_ADDRESS 0xf1
716902ca74Sjmcneill #define PSEYE_SCCB_SUBADDR 0xf2
726902ca74Sjmcneill #define PSEYE_SCCB_WRITE 0xf3
736902ca74Sjmcneill #define PSEYE_SCCB_READ 0xf4
746902ca74Sjmcneill #define PSEYE_SCCB_OPERATION 0xf5
756902ca74Sjmcneill #define PSEYE_SCCB_STATUS 0xf6
766902ca74Sjmcneill
776902ca74Sjmcneill #define PSEYE_SCCB_OP_WRITE_3 0x37
786902ca74Sjmcneill #define PSEYE_SCCB_OP_WRITE_2 0x33
796902ca74Sjmcneill #define PSEYE_SCCB_OP_READ_2 0xf9
806902ca74Sjmcneill
816902ca74Sjmcneill struct pseye_softc {
8249337c88Sdyoung device_t sc_dev;
836902ca74Sjmcneill
844e8e6643Sskrll struct usbd_device * sc_udev;
854e8e6643Sskrll struct usbd_interface * sc_iface;
866902ca74Sjmcneill
876902ca74Sjmcneill device_t sc_videodev;
886902ca74Sjmcneill char sc_running;
896902ca74Sjmcneill
906902ca74Sjmcneill kcondvar_t sc_cv;
916902ca74Sjmcneill kmutex_t sc_mtx;
926902ca74Sjmcneill
934e8e6643Sskrll struct usbd_pipe * sc_bulkin_pipe;
944e8e6643Sskrll struct usbd_xfer * sc_bulkin_xfer;
956902ca74Sjmcneill int sc_bulkin;
966902ca74Sjmcneill uint8_t *sc_bulkin_buffer;
976902ca74Sjmcneill int sc_bulkin_bufferlen;
986902ca74Sjmcneill
996902ca74Sjmcneill char sc_dying;
100df0c38f6Sjmcneill
101df0c38f6Sjmcneill char sc_businfo[32];
1026902ca74Sjmcneill };
1036902ca74Sjmcneill
1046902ca74Sjmcneill static int pseye_match(device_t, cfdata_t, void *);
1056902ca74Sjmcneill static void pseye_attach(device_t, device_t, void *);
1066902ca74Sjmcneill static int pseye_detach(device_t, int);
1076902ca74Sjmcneill static void pseye_childdet(device_t, device_t);
1086902ca74Sjmcneill static int pseye_activate(device_t, enum devact);
1096902ca74Sjmcneill
1106902ca74Sjmcneill static void pseye_init(struct pseye_softc *);
1116902ca74Sjmcneill static void pseye_sccb_init(struct pseye_softc *);
1126902ca74Sjmcneill static void pseye_stop(struct pseye_softc *);
1136902ca74Sjmcneill static void pseye_start(struct pseye_softc *);
1146902ca74Sjmcneill static void pseye_led(struct pseye_softc *, bool);
1156902ca74Sjmcneill static uint8_t pseye_getreg(struct pseye_softc *, uint16_t);
1166902ca74Sjmcneill static void pseye_setreg(struct pseye_softc *, uint16_t, uint8_t);
1176902ca74Sjmcneill static void pseye_setregv(struct pseye_softc *, uint16_t, uint8_t);
1186902ca74Sjmcneill static void pseye_sccb_setreg(struct pseye_softc *, uint8_t, uint8_t);
1196902ca74Sjmcneill static bool pseye_sccb_status(struct pseye_softc *);
1206902ca74Sjmcneill
1216902ca74Sjmcneill static int pseye_init_pipes(struct pseye_softc *);
1226902ca74Sjmcneill static int pseye_close_pipes(struct pseye_softc *);
1236902ca74Sjmcneill
1246277e868Sjmcneill static usbd_status pseye_get_frame(struct pseye_softc *, uint32_t *);
1256277e868Sjmcneill static void pseye_submit_payload(struct pseye_softc *, uint32_t);
1266902ca74Sjmcneill
1276902ca74Sjmcneill /* video(9) API */
1286902ca74Sjmcneill static int pseye_open(void *, int);
1296902ca74Sjmcneill static void pseye_close(void *);
1306902ca74Sjmcneill static const char * pseye_get_devname(void *);
131df0c38f6Sjmcneill static const char * pseye_get_businfo(void *);
13294e5d88fSjmcneill static int pseye_enum_format(void *, uint32_t,
13394e5d88fSjmcneill struct video_format *);
1346902ca74Sjmcneill static int pseye_get_format(void *, struct video_format *);
1356902ca74Sjmcneill static int pseye_set_format(void *, struct video_format *);
136dd848685Sjmcneill static int pseye_try_format(void *, struct video_format *);
137bb520deeSjmcneill static int pseye_get_framerate(void *, struct video_fract *);
138bb520deeSjmcneill static int pseye_set_framerate(void *, struct video_fract *);
1396902ca74Sjmcneill static int pseye_start_transfer(void *);
1406902ca74Sjmcneill static int pseye_stop_transfer(void *);
1416902ca74Sjmcneill
1426902ca74Sjmcneill CFATTACH_DECL2_NEW(pseye, sizeof(struct pseye_softc),
1436902ca74Sjmcneill pseye_match, pseye_attach, pseye_detach, pseye_activate,
1446902ca74Sjmcneill NULL, pseye_childdet);
1456902ca74Sjmcneill
1466902ca74Sjmcneill static const struct video_hw_if pseye_hw_if = {
1476902ca74Sjmcneill .open = pseye_open,
1486902ca74Sjmcneill .close = pseye_close,
1496902ca74Sjmcneill .get_devname = pseye_get_devname,
150df0c38f6Sjmcneill .get_businfo = pseye_get_businfo,
15194e5d88fSjmcneill .enum_format = pseye_enum_format,
1526902ca74Sjmcneill .get_format = pseye_get_format,
1536902ca74Sjmcneill .set_format = pseye_set_format,
154dd848685Sjmcneill .try_format = pseye_try_format,
155bb520deeSjmcneill .get_framerate = pseye_get_framerate,
156bb520deeSjmcneill .set_framerate = pseye_set_framerate,
1576902ca74Sjmcneill .start_transfer = pseye_start_transfer,
1586902ca74Sjmcneill .stop_transfer = pseye_stop_transfer,
1596902ca74Sjmcneill .control_iter_init = NULL,
1606902ca74Sjmcneill .control_iter_next = NULL,
1616902ca74Sjmcneill .get_control_desc_group = NULL,
1626902ca74Sjmcneill .get_control_group = NULL,
1636902ca74Sjmcneill .set_control_group = NULL,
1646902ca74Sjmcneill };
1656902ca74Sjmcneill
1662941ba2cSjmcneill static int
pseye_match(device_t parent,cfdata_t match,void * opaque)1672941ba2cSjmcneill pseye_match(device_t parent, cfdata_t match, void *opaque)
1686902ca74Sjmcneill {
1694e8e6643Sskrll struct usbif_attach_arg *uiaa = opaque;
1706902ca74Sjmcneill
1714e8e6643Sskrll if (uiaa->uiaa_class != UICLASS_VENDOR)
1726902ca74Sjmcneill return UMATCH_NONE;
1736902ca74Sjmcneill
1744e8e6643Sskrll if (uiaa->uiaa_vendor == USB_VENDOR_OMNIVISION2) {
1754e8e6643Sskrll switch (uiaa->uiaa_product) {
1766902ca74Sjmcneill case USB_PRODUCT_OMNIVISION2_PSEYE:
1774e8e6643Sskrll if (uiaa->uiaa_ifaceno != 0)
1786902ca74Sjmcneill return UMATCH_NONE;
1796902ca74Sjmcneill return UMATCH_VENDOR_PRODUCT;
1806902ca74Sjmcneill }
1816902ca74Sjmcneill }
1826902ca74Sjmcneill
1836902ca74Sjmcneill return UMATCH_NONE;
1846902ca74Sjmcneill }
1856902ca74Sjmcneill
1862941ba2cSjmcneill static void
pseye_attach(device_t parent,device_t self,void * opaque)1872941ba2cSjmcneill pseye_attach(device_t parent, device_t self, void *opaque)
1886902ca74Sjmcneill {
1892941ba2cSjmcneill struct pseye_softc *sc = device_private(self);
1904e8e6643Sskrll struct usbif_attach_arg *uiaa = opaque;
1914e8e6643Sskrll struct usbd_device *dev = uiaa->uiaa_device;
1926902ca74Sjmcneill usb_interface_descriptor_t *id = NULL;
1936902ca74Sjmcneill usb_endpoint_descriptor_t *ed = NULL, *ed_bulkin = NULL;
1945fab894cSplunky char *devinfop;
1956902ca74Sjmcneill int i;
1966902ca74Sjmcneill
1972941ba2cSjmcneill aprint_naive("\n");
1985fab894cSplunky aprint_normal("\n");
1995fab894cSplunky
2005fab894cSplunky devinfop = usbd_devinfo_alloc(dev, 0);
2015fab894cSplunky aprint_normal_dev(self, "%s\n", devinfop);
2025fab894cSplunky usbd_devinfo_free(devinfop);
2036902ca74Sjmcneill
2046902ca74Sjmcneill sc->sc_dev = self;
2056902ca74Sjmcneill sc->sc_udev = dev;
2064e8e6643Sskrll sc->sc_iface = uiaa->uiaa_iface;
207df0c38f6Sjmcneill snprintf(sc->sc_businfo, sizeof(sc->sc_businfo), "usb:%08x",
2084e8e6643Sskrll sc->sc_udev->ud_cookie.cookie);
2096902ca74Sjmcneill sc->sc_bulkin_bufferlen = PSEYE_BULKIN_BUFLEN;
2106902ca74Sjmcneill
2116902ca74Sjmcneill sc->sc_dying = sc->sc_running = 0;
212c3641af7Sjmcneill cv_init(&sc->sc_cv, device_xname(self));
2136902ca74Sjmcneill mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);
2146902ca74Sjmcneill
2156902ca74Sjmcneill id = usbd_get_interface_descriptor(sc->sc_iface);
2166902ca74Sjmcneill if (id == NULL) {
2176902ca74Sjmcneill aprint_error_dev(self, "failed to get interface descriptor\n");
2186902ca74Sjmcneill sc->sc_dying = 1;
2192941ba2cSjmcneill return;
2206902ca74Sjmcneill }
2216902ca74Sjmcneill
2226902ca74Sjmcneill for (i = 0; i < id->bNumEndpoints; i++) {
2236902ca74Sjmcneill ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
2246902ca74Sjmcneill if (ed == NULL) {
225c3641af7Sjmcneill aprint_error_dev(self, "couldn't get ep %d\n", i);
2266902ca74Sjmcneill sc->sc_dying = 1;
2272941ba2cSjmcneill return;
2286902ca74Sjmcneill }
2296902ca74Sjmcneill
2306902ca74Sjmcneill if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
2316902ca74Sjmcneill UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
2326902ca74Sjmcneill ed_bulkin = ed;
2336902ca74Sjmcneill break;
2346902ca74Sjmcneill }
2356902ca74Sjmcneill }
2366902ca74Sjmcneill
2376902ca74Sjmcneill if (ed_bulkin == NULL) {
238c3641af7Sjmcneill aprint_error_dev(self, "no bulk-in endpoint found\n");
2396902ca74Sjmcneill sc->sc_dying = 1;
2402941ba2cSjmcneill return;
2416902ca74Sjmcneill }
2426902ca74Sjmcneill
2436902ca74Sjmcneill sc->sc_bulkin = ed_bulkin->bEndpointAddress;
2446902ca74Sjmcneill
2454e8e6643Sskrll int error = pseye_init_pipes(sc);
2464e8e6643Sskrll if (error) {
2474e8e6643Sskrll aprint_error_dev(self, "couldn't open pipes\n");
2482941ba2cSjmcneill return;
2496902ca74Sjmcneill }
2504e8e6643Sskrll
2514e8e6643Sskrll error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_bulkin_bufferlen,
252b8421611Sskrll 0, 0, &sc->sc_bulkin_xfer);
2534e8e6643Sskrll if (error) {
2544e8e6643Sskrll aprint_error_dev(self, "couldn't create transfer\n");
2554e8e6643Sskrll pseye_close_pipes(sc);
2562941ba2cSjmcneill return;
2576902ca74Sjmcneill }
2586902ca74Sjmcneill
2594e8e6643Sskrll sc->sc_bulkin_buffer = usbd_get_buffer(sc->sc_bulkin_xfer);
2604e8e6643Sskrll
2616902ca74Sjmcneill pseye_init(sc);
2626902ca74Sjmcneill
263c3641af7Sjmcneill if (!pmf_device_register(self, NULL, NULL))
264c3641af7Sjmcneill aprint_error_dev(self, "couldn't establish power handler\n");
265c3641af7Sjmcneill
266*322f3613Sriastradh sc->sc_videodev = video_attach_mi(&pseye_hw_if, self, sc);
2676902ca74Sjmcneill if (sc->sc_videodev == NULL) {
268c3641af7Sjmcneill aprint_error_dev(self, "couldn't attach video layer\n");
2696902ca74Sjmcneill sc->sc_dying = 1;
2702941ba2cSjmcneill return;
2716902ca74Sjmcneill }
2726902ca74Sjmcneill
2738bc54e5bSmsaitoh usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, self);
274fc7511b0Swrstuden
2756902ca74Sjmcneill }
2766902ca74Sjmcneill
2772941ba2cSjmcneill static int
pseye_detach(device_t self,int flags)2782941ba2cSjmcneill pseye_detach(device_t self, int flags)
2796902ca74Sjmcneill {
2802941ba2cSjmcneill struct pseye_softc *sc = device_private(self);
2816902ca74Sjmcneill
2826902ca74Sjmcneill sc->sc_dying = 1;
2836902ca74Sjmcneill
284c3641af7Sjmcneill pmf_device_deregister(self);
285c3641af7Sjmcneill
2866902ca74Sjmcneill if (sc->sc_videodev != NULL) {
2876902ca74Sjmcneill config_detach(sc->sc_videodev, flags);
2886902ca74Sjmcneill sc->sc_videodev = NULL;
2896902ca74Sjmcneill }
2906902ca74Sjmcneill
2914e8e6643Sskrll if (sc->sc_bulkin_pipe != NULL) {
2924e8e6643Sskrll usbd_abort_pipe(sc->sc_bulkin_pipe);
2934e8e6643Sskrll }
2944e8e6643Sskrll
2956902ca74Sjmcneill if (sc->sc_bulkin_xfer != NULL) {
2964e8e6643Sskrll usbd_destroy_xfer(sc->sc_bulkin_xfer);
2976902ca74Sjmcneill sc->sc_bulkin_xfer = NULL;
2986902ca74Sjmcneill }
2996902ca74Sjmcneill
3006902ca74Sjmcneill if (sc->sc_bulkin_pipe != NULL) {
3014e8e6643Sskrll usbd_close_pipe(sc->sc_bulkin_pipe);
3026902ca74Sjmcneill sc->sc_bulkin_pipe = NULL;
3036902ca74Sjmcneill }
3046902ca74Sjmcneill
3056902ca74Sjmcneill mutex_enter(&sc->sc_mtx);
3066902ca74Sjmcneill if (sc->sc_running) {
3076902ca74Sjmcneill sc->sc_running = 0;
3086902ca74Sjmcneill cv_wait_sig(&sc->sc_cv, &sc->sc_mtx);
3096902ca74Sjmcneill }
3106902ca74Sjmcneill mutex_exit(&sc->sc_mtx);
3116902ca74Sjmcneill
3126902ca74Sjmcneill cv_destroy(&sc->sc_cv);
3136902ca74Sjmcneill mutex_destroy(&sc->sc_mtx);
3146902ca74Sjmcneill
3158bc54e5bSmsaitoh usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
3166902ca74Sjmcneill
3176902ca74Sjmcneill return 0;
3186902ca74Sjmcneill }
3196902ca74Sjmcneill
3206902ca74Sjmcneill int
pseye_activate(device_t self,enum devact act)32149337c88Sdyoung pseye_activate(device_t self, enum devact act)
3226902ca74Sjmcneill {
3236902ca74Sjmcneill struct pseye_softc *sc = device_private(self);
3246902ca74Sjmcneill
3256902ca74Sjmcneill switch (act) {
3266902ca74Sjmcneill case DVACT_DEACTIVATE:
3276902ca74Sjmcneill sc->sc_dying = 1;
328efc6bfe7Sdyoung return 0;
329efc6bfe7Sdyoung default:
330efc6bfe7Sdyoung return EOPNOTSUPP;
3316902ca74Sjmcneill }
3326902ca74Sjmcneill }
3336902ca74Sjmcneill
3346902ca74Sjmcneill static void
pseye_childdet(device_t self,device_t child)3356902ca74Sjmcneill pseye_childdet(device_t self, device_t child)
3366902ca74Sjmcneill {
3376902ca74Sjmcneill struct pseye_softc *sc = device_private(self);
3386902ca74Sjmcneill
3396902ca74Sjmcneill if (sc->sc_videodev) {
3406902ca74Sjmcneill KASSERT(sc->sc_videodev == child);
3416902ca74Sjmcneill sc->sc_videodev = NULL;
3426902ca74Sjmcneill }
3436902ca74Sjmcneill }
3446902ca74Sjmcneill
3456902ca74Sjmcneill /*
3466902ca74Sjmcneill * Device access
3476902ca74Sjmcneill */
3486902ca74Sjmcneill
3496902ca74Sjmcneill static void
pseye_init(struct pseye_softc * sc)3506902ca74Sjmcneill pseye_init(struct pseye_softc *sc)
3516902ca74Sjmcneill {
3526902ca74Sjmcneill pseye_sccb_init(sc);
3536902ca74Sjmcneill
3546902ca74Sjmcneill pseye_setregv(sc, 0xc2, 0x0c);
3556902ca74Sjmcneill pseye_setregv(sc, 0x88, 0xf8);
3566902ca74Sjmcneill pseye_setregv(sc, 0xc3, 0x69);
3576902ca74Sjmcneill pseye_setregv(sc, 0x89, 0xff);
3586902ca74Sjmcneill pseye_setregv(sc, 0x76, 0x03);
3596902ca74Sjmcneill pseye_setregv(sc, 0x92, 0x01);
3606902ca74Sjmcneill pseye_setregv(sc, 0x93, 0x18);
3616902ca74Sjmcneill pseye_setregv(sc, 0x94, 0x10);
3626902ca74Sjmcneill pseye_setregv(sc, 0x95, 0x10);
3636902ca74Sjmcneill pseye_setregv(sc, 0xe2, 0x00);
3646902ca74Sjmcneill pseye_setregv(sc, 0xe7, 0x3e);
3656902ca74Sjmcneill
3666902ca74Sjmcneill pseye_setregv(sc, 0x96, 0x00);
3676902ca74Sjmcneill
3686902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x20);
3696902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x20);
3706902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x20);
3716902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x0a);
3726902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x3f);
3736902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x4a);
3746902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x20);
3756902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x15);
3766902ca74Sjmcneill pseye_setreg(sc, 0x97, 0x0b);
3776902ca74Sjmcneill
3786902ca74Sjmcneill pseye_setregv(sc, 0x8e, 0x40);
3796902ca74Sjmcneill pseye_setregv(sc, 0x1f, 0x81);
3806902ca74Sjmcneill pseye_setregv(sc, 0x34, 0x05);
3816902ca74Sjmcneill pseye_setregv(sc, 0xe3, 0x04);
3826902ca74Sjmcneill pseye_setregv(sc, 0x88, 0x00);
3836902ca74Sjmcneill pseye_setregv(sc, 0x89, 0x00);
3846902ca74Sjmcneill pseye_setregv(sc, 0x76, 0x00);
3856902ca74Sjmcneill pseye_setregv(sc, 0xe7, 0x2e);
3866902ca74Sjmcneill pseye_setregv(sc, 0x31, 0xf9);
3876902ca74Sjmcneill pseye_setregv(sc, 0x25, 0x42);
3886902ca74Sjmcneill pseye_setregv(sc, 0x21, 0xf0);
3896902ca74Sjmcneill
3906902ca74Sjmcneill pseye_setreg(sc, 0x1c, 0x00);
3916902ca74Sjmcneill pseye_setreg(sc, 0x1d, 0x40);
3926277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x02); /* payload size 0x0200 * 4 == 2048 */
3936902ca74Sjmcneill pseye_setreg(sc, 0x1d, 0x00);
3946277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x02); /* frame size 0x025800 * 4 == 614400 */
3956277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x58);
3966277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x00);
3976277e868Sjmcneill
3986277e868Sjmcneill pseye_setreg(sc, 0x1c, 0x0a);
3996277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x08); /* enable UVC header */
4006277e868Sjmcneill pseye_setreg(sc, 0x1d, 0x0e);
4016902ca74Sjmcneill
4026902ca74Sjmcneill pseye_setregv(sc, 0x8d, 0x1c);
4036902ca74Sjmcneill pseye_setregv(sc, 0x8e, 0x80);
4046902ca74Sjmcneill pseye_setregv(sc, 0xe5, 0x04);
4056902ca74Sjmcneill
4066902ca74Sjmcneill pseye_sccb_setreg(sc, 0x12, 0x80);
4076902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4086902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4096902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4106902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4116902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4126902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4136902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4146902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4156902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4166902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4176902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4186902ca74Sjmcneill
4196902ca74Sjmcneill pseye_sccb_setreg(sc, 0x3d, 0x03);
4206902ca74Sjmcneill pseye_sccb_setreg(sc, 0x17, 0x26);
4216902ca74Sjmcneill pseye_sccb_setreg(sc, 0x18, 0xa0);
4226902ca74Sjmcneill pseye_sccb_setreg(sc, 0x19, 0x07);
4236902ca74Sjmcneill pseye_sccb_setreg(sc, 0x1a, 0xf0);
4246902ca74Sjmcneill pseye_sccb_setreg(sc, 0x32, 0x00);
4256902ca74Sjmcneill pseye_sccb_setreg(sc, 0x29, 0xa0);
4266902ca74Sjmcneill pseye_sccb_setreg(sc, 0x2c, 0xf0);
4276902ca74Sjmcneill pseye_sccb_setreg(sc, 0x65, 0x20);
4286902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4296902ca74Sjmcneill pseye_sccb_setreg(sc, 0x42, 0x7f);
4306902ca74Sjmcneill pseye_sccb_setreg(sc, 0x63, 0xe0);
4316902ca74Sjmcneill pseye_sccb_setreg(sc, 0x64, 0xff);
4326902ca74Sjmcneill pseye_sccb_setreg(sc, 0x66, 0x00);
4336902ca74Sjmcneill pseye_sccb_setreg(sc, 0x13, 0xf0);
4346902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0d, 0x41);
4356902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0f, 0xc5);
4366902ca74Sjmcneill pseye_sccb_setreg(sc, 0x14, 0x11);
4376902ca74Sjmcneill
4386902ca74Sjmcneill pseye_sccb_setreg(sc, 0x22, 0x7f);
4396902ca74Sjmcneill pseye_sccb_setreg(sc, 0x23, 0x03);
4406902ca74Sjmcneill pseye_sccb_setreg(sc, 0x24, 0x40);
4416902ca74Sjmcneill pseye_sccb_setreg(sc, 0x25, 0x30);
4426902ca74Sjmcneill pseye_sccb_setreg(sc, 0x26, 0xa1);
4436902ca74Sjmcneill pseye_sccb_setreg(sc, 0x2a, 0x00);
4446902ca74Sjmcneill pseye_sccb_setreg(sc, 0x2b, 0x00);
4456902ca74Sjmcneill pseye_sccb_setreg(sc, 0x6b, 0xaa);
4466902ca74Sjmcneill pseye_sccb_setreg(sc, 0x13, 0xff);
4476902ca74Sjmcneill
4486902ca74Sjmcneill pseye_sccb_setreg(sc, 0x90, 0x05);
4496902ca74Sjmcneill pseye_sccb_setreg(sc, 0x91, 0x01);
4506902ca74Sjmcneill pseye_sccb_setreg(sc, 0x92, 0x03);
4516902ca74Sjmcneill pseye_sccb_setreg(sc, 0x93, 0x00);
4526902ca74Sjmcneill pseye_sccb_setreg(sc, 0x94, 0x60);
4536902ca74Sjmcneill pseye_sccb_setreg(sc, 0x95, 0x3c);
4546902ca74Sjmcneill pseye_sccb_setreg(sc, 0x96, 0x24);
4556902ca74Sjmcneill pseye_sccb_setreg(sc, 0x97, 0x1e);
4566902ca74Sjmcneill pseye_sccb_setreg(sc, 0x98, 0x62);
4576902ca74Sjmcneill pseye_sccb_setreg(sc, 0x99, 0x80);
4586902ca74Sjmcneill pseye_sccb_setreg(sc, 0x9a, 0x1e);
4596902ca74Sjmcneill pseye_sccb_setreg(sc, 0x9b, 0x08);
4606902ca74Sjmcneill pseye_sccb_setreg(sc, 0x9c, 0x20);
4616902ca74Sjmcneill pseye_sccb_setreg(sc, 0x9e, 0x81);
4626902ca74Sjmcneill
4636902ca74Sjmcneill pseye_sccb_setreg(sc, 0xa6, 0x04);
4646902ca74Sjmcneill pseye_sccb_setreg(sc, 0x7e, 0x0c);
4656902ca74Sjmcneill pseye_sccb_setreg(sc, 0x7f, 0x16);
4666902ca74Sjmcneill
4676902ca74Sjmcneill pseye_sccb_setreg(sc, 0x80, 0x2a);
4686902ca74Sjmcneill pseye_sccb_setreg(sc, 0x81, 0x4e);
4696902ca74Sjmcneill pseye_sccb_setreg(sc, 0x82, 0x61);
4706902ca74Sjmcneill pseye_sccb_setreg(sc, 0x83, 0x6f);
4716902ca74Sjmcneill pseye_sccb_setreg(sc, 0x84, 0x7b);
4726902ca74Sjmcneill pseye_sccb_setreg(sc, 0x85, 0x86);
4736902ca74Sjmcneill pseye_sccb_setreg(sc, 0x86, 0x8e);
4746902ca74Sjmcneill pseye_sccb_setreg(sc, 0x87, 0x97);
4756902ca74Sjmcneill pseye_sccb_setreg(sc, 0x88, 0xa4);
4766902ca74Sjmcneill pseye_sccb_setreg(sc, 0x89, 0xaf);
4776902ca74Sjmcneill pseye_sccb_setreg(sc, 0x8a, 0xc5);
4786902ca74Sjmcneill pseye_sccb_setreg(sc, 0x8b, 0xd7);
4796902ca74Sjmcneill pseye_sccb_setreg(sc, 0x8c, 0xe8);
4806902ca74Sjmcneill pseye_sccb_setreg(sc, 0x8d, 0x20);
4816902ca74Sjmcneill
4826902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0c, 0x90);
4836902ca74Sjmcneill
4846902ca74Sjmcneill pseye_setregv(sc, 0xc0, 0x50);
4856902ca74Sjmcneill pseye_setregv(sc, 0xc1, 0x3c);
4866902ca74Sjmcneill pseye_setregv(sc, 0xc2, 0x0c);
4876902ca74Sjmcneill
4886902ca74Sjmcneill pseye_sccb_setreg(sc, 0x2b, 0x00);
4896902ca74Sjmcneill pseye_sccb_setreg(sc, 0x22, 0x7f);
4906902ca74Sjmcneill pseye_sccb_setreg(sc, 0x23, 0x03);
4916902ca74Sjmcneill pseye_sccb_setreg(sc, 0x11, 0x01);
4926902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0c, 0xd0);
4936902ca74Sjmcneill pseye_sccb_setreg(sc, 0x64, 0xff);
4946902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0d, 0x41);
4956902ca74Sjmcneill
4966902ca74Sjmcneill pseye_sccb_setreg(sc, 0x14, 0x41);
4976902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0e, 0xcd);
4986902ca74Sjmcneill pseye_sccb_setreg(sc, 0xac, 0xbf);
4996902ca74Sjmcneill pseye_sccb_setreg(sc, 0x8e, 0x00);
5006902ca74Sjmcneill pseye_sccb_setreg(sc, 0x0c, 0xd0);
5016902ca74Sjmcneill
5026902ca74Sjmcneill pseye_stop(sc);
5036902ca74Sjmcneill }
5046902ca74Sjmcneill
5056902ca74Sjmcneill static void
pseye_sccb_init(struct pseye_softc * sc)5066902ca74Sjmcneill pseye_sccb_init(struct pseye_softc *sc)
5076902ca74Sjmcneill {
5086902ca74Sjmcneill pseye_setregv(sc, 0xe7, 0x3a);
5096902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60);
5106902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60);
5116902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60);
5126902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x42);
5136902ca74Sjmcneill }
5146902ca74Sjmcneill
5156902ca74Sjmcneill static void
pseye_stop(struct pseye_softc * sc)5166902ca74Sjmcneill pseye_stop(struct pseye_softc *sc)
5176902ca74Sjmcneill {
5186902ca74Sjmcneill pseye_led(sc, false);
5196902ca74Sjmcneill pseye_setreg(sc, 0xe0, 0x09);
5206902ca74Sjmcneill }
5216902ca74Sjmcneill
5226902ca74Sjmcneill static void
pseye_start(struct pseye_softc * sc)5236902ca74Sjmcneill pseye_start(struct pseye_softc *sc)
5246902ca74Sjmcneill {
5256902ca74Sjmcneill pseye_led(sc, true);
5266902ca74Sjmcneill pseye_setreg(sc, 0xe0, 0x00);
5276902ca74Sjmcneill }
5286902ca74Sjmcneill
5296902ca74Sjmcneill static void
pseye_led(struct pseye_softc * sc,bool enabled)5306902ca74Sjmcneill pseye_led(struct pseye_softc *sc, bool enabled)
5316902ca74Sjmcneill {
5326902ca74Sjmcneill uint8_t val;
5336902ca74Sjmcneill
5346902ca74Sjmcneill val = pseye_getreg(sc, 0x21);
5356902ca74Sjmcneill pseye_setreg(sc, 0x21, val | 0x80);
5366902ca74Sjmcneill
5376902ca74Sjmcneill val = pseye_getreg(sc, 0x23);
5386902ca74Sjmcneill if (enabled == true)
5396902ca74Sjmcneill val |= 0x80;
5406902ca74Sjmcneill else
5416902ca74Sjmcneill val &= ~0x80;
5426902ca74Sjmcneill pseye_setreg(sc, 0x23, val);
5436902ca74Sjmcneill }
5446902ca74Sjmcneill
5456902ca74Sjmcneill static uint8_t
pseye_getreg(struct pseye_softc * sc,uint16_t reg)5466902ca74Sjmcneill pseye_getreg(struct pseye_softc *sc, uint16_t reg)
5476902ca74Sjmcneill {
5486902ca74Sjmcneill usb_device_request_t req;
5496902ca74Sjmcneill usbd_status err;
5506902ca74Sjmcneill uint8_t buf;
5516902ca74Sjmcneill
5526902ca74Sjmcneill req.bmRequestType = UT_READ_VENDOR_DEVICE;
5536902ca74Sjmcneill req.bRequest = 1;
5546902ca74Sjmcneill USETW(req.wValue, 0x0000);
5556902ca74Sjmcneill USETW(req.wIndex, reg);
5566902ca74Sjmcneill USETW(req.wLength, 1);
5576902ca74Sjmcneill
5586902ca74Sjmcneill err = usbd_do_request(sc->sc_udev, &req, &buf);
5596902ca74Sjmcneill if (err) {
560aa3d6ec1Schristos aprint_error_dev(sc->sc_dev, "couldn't read reg 0x%04x: %s\n",
5616902ca74Sjmcneill reg, usbd_errstr(err));
5626902ca74Sjmcneill return 0xff;
5636902ca74Sjmcneill }
5646902ca74Sjmcneill
5656902ca74Sjmcneill return buf;
5666902ca74Sjmcneill }
5676902ca74Sjmcneill
5686902ca74Sjmcneill static void
pseye_setreg(struct pseye_softc * sc,uint16_t reg,uint8_t val)5696902ca74Sjmcneill pseye_setreg(struct pseye_softc *sc, uint16_t reg, uint8_t val)
5706902ca74Sjmcneill {
5716902ca74Sjmcneill usb_device_request_t req;
5726902ca74Sjmcneill usbd_status err;
5736902ca74Sjmcneill
5746902ca74Sjmcneill req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
5756902ca74Sjmcneill req.bRequest = 1;
5766902ca74Sjmcneill USETW(req.wValue, 0x0000);
5776902ca74Sjmcneill USETW(req.wIndex, reg);
5786902ca74Sjmcneill USETW(req.wLength, 1);
5796902ca74Sjmcneill
5806902ca74Sjmcneill err = usbd_do_request(sc->sc_udev, &req, &val);
5816902ca74Sjmcneill if (err)
582aa3d6ec1Schristos aprint_error_dev(sc->sc_dev, "couldn't write reg 0x%04x: %s\n",
5836902ca74Sjmcneill reg, usbd_errstr(err));
5846902ca74Sjmcneill }
5856902ca74Sjmcneill
5866902ca74Sjmcneill static void
pseye_setregv(struct pseye_softc * sc,uint16_t reg,uint8_t val)5876902ca74Sjmcneill pseye_setregv(struct pseye_softc *sc, uint16_t reg, uint8_t val)
5886902ca74Sjmcneill {
5896902ca74Sjmcneill pseye_setreg(sc, reg, val);
5906902ca74Sjmcneill if (pseye_getreg(sc, reg) != val)
591aa3d6ec1Schristos aprint_error_dev(sc->sc_dev, "couldn't verify reg 0x%04x\n",
5926902ca74Sjmcneill reg);
5936902ca74Sjmcneill }
5946902ca74Sjmcneill
5956902ca74Sjmcneill static void
pseye_sccb_setreg(struct pseye_softc * sc,uint8_t reg,uint8_t val)5966902ca74Sjmcneill pseye_sccb_setreg(struct pseye_softc *sc, uint8_t reg, uint8_t val)
5976902ca74Sjmcneill {
5986902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_SUBADDR, reg);
5996902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_WRITE, val);
6006902ca74Sjmcneill pseye_setreg(sc, PSEYE_SCCB_OPERATION, PSEYE_SCCB_OP_WRITE_3);
6016902ca74Sjmcneill
6026902ca74Sjmcneill if (pseye_sccb_status(sc) == false)
603aa3d6ec1Schristos aprint_error_dev(sc->sc_dev, "couldn't write sccb reg 0x%04x\n",
6046902ca74Sjmcneill reg);
6056902ca74Sjmcneill }
6066902ca74Sjmcneill
6076902ca74Sjmcneill static bool
pseye_sccb_status(struct pseye_softc * sc)6086902ca74Sjmcneill pseye_sccb_status(struct pseye_softc *sc)
6096902ca74Sjmcneill {
6106902ca74Sjmcneill int retry = 5;
6116902ca74Sjmcneill uint8_t reg;
6126902ca74Sjmcneill
6136902ca74Sjmcneill while (retry-- >= 0) {
6146902ca74Sjmcneill reg = pseye_getreg(sc, PSEYE_SCCB_STATUS);
6156902ca74Sjmcneill if (reg == 0x00)
6166902ca74Sjmcneill return true;
6176902ca74Sjmcneill else if (reg == 0x04)
6186902ca74Sjmcneill return false;
6196902ca74Sjmcneill }
6206902ca74Sjmcneill
6216902ca74Sjmcneill aprint_error_dev(sc->sc_dev, "timeout reading sccb status\n");
6226902ca74Sjmcneill return false;
6236902ca74Sjmcneill }
6246902ca74Sjmcneill
6256902ca74Sjmcneill static usbd_status
pseye_get_frame(struct pseye_softc * sc,uint32_t * plen)6266277e868Sjmcneill pseye_get_frame(struct pseye_softc *sc, uint32_t *plen)
6276902ca74Sjmcneill {
6286902ca74Sjmcneill if (sc->sc_dying)
6296902ca74Sjmcneill return USBD_IOERROR;
6306902ca74Sjmcneill
6316902ca74Sjmcneill return usbd_bulk_transfer(sc->sc_bulkin_xfer, sc->sc_bulkin_pipe,
6324e8e6643Sskrll USBD_SHORT_XFER_OK, 1000, sc->sc_bulkin_buffer, plen);
6336902ca74Sjmcneill }
6346902ca74Sjmcneill
6356902ca74Sjmcneill static int
pseye_init_pipes(struct pseye_softc * sc)6366902ca74Sjmcneill pseye_init_pipes(struct pseye_softc *sc)
6376902ca74Sjmcneill {
6386902ca74Sjmcneill usbd_status err;
6396902ca74Sjmcneill
6406902ca74Sjmcneill if (sc->sc_dying)
6416902ca74Sjmcneill return EIO;
6426902ca74Sjmcneill
6436902ca74Sjmcneill err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin, 0,
6446902ca74Sjmcneill &sc->sc_bulkin_pipe);
6456902ca74Sjmcneill if (err) {
6466902ca74Sjmcneill aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n",
6476902ca74Sjmcneill usbd_errstr(err));
6486902ca74Sjmcneill return ENOMEM;
6496902ca74Sjmcneill }
6506902ca74Sjmcneill
6516902ca74Sjmcneill return 0;
6526902ca74Sjmcneill }
6536902ca74Sjmcneill
6546902ca74Sjmcneill int
pseye_close_pipes(struct pseye_softc * sc)6556902ca74Sjmcneill pseye_close_pipes(struct pseye_softc *sc)
6566902ca74Sjmcneill {
6576902ca74Sjmcneill if (sc->sc_bulkin_pipe != NULL) {
6586902ca74Sjmcneill usbd_abort_pipe(sc->sc_bulkin_pipe);
6596902ca74Sjmcneill usbd_close_pipe(sc->sc_bulkin_pipe);
6606902ca74Sjmcneill sc->sc_bulkin_pipe = NULL;
6616902ca74Sjmcneill }
6626902ca74Sjmcneill
6636902ca74Sjmcneill return 0;
6646902ca74Sjmcneill }
6656902ca74Sjmcneill
6666902ca74Sjmcneill static void
pseye_submit_payload(struct pseye_softc * sc,uint32_t tlen)6676277e868Sjmcneill pseye_submit_payload(struct pseye_softc *sc, uint32_t tlen)
6686277e868Sjmcneill {
6696277e868Sjmcneill struct video_payload payload;
6706277e868Sjmcneill uvideo_payload_header_t *uvchdr;
6716277e868Sjmcneill uint8_t *buf = sc->sc_bulkin_buffer;
6726277e868Sjmcneill uint32_t len;
6736277e868Sjmcneill uint32_t brem = (640*480*2);
6746277e868Sjmcneill
6756277e868Sjmcneill while (brem > 0 && tlen > 0) {
676d1579b2dSriastradh len = uimin(tlen, PSEYE_BULKIN_BLKLEN);
6776277e868Sjmcneill if (len < UVIDEO_PAYLOAD_HEADER_SIZE) {
6786277e868Sjmcneill printf("pseye_submit_payload: len=%u\n", len);
6796277e868Sjmcneill return;
6806277e868Sjmcneill }
6816277e868Sjmcneill
6826277e868Sjmcneill uvchdr = (uvideo_payload_header_t *)buf;
6836277e868Sjmcneill if (uvchdr->bHeaderLength != UVIDEO_PAYLOAD_HEADER_SIZE)
6846277e868Sjmcneill goto next;
6856277e868Sjmcneill if (uvchdr->bHeaderLength == len &&
6866277e868Sjmcneill !(uvchdr->bmHeaderInfo & UV_END_OF_FRAME))
6876277e868Sjmcneill goto next;
6886277e868Sjmcneill if (uvchdr->bmHeaderInfo & UV_ERROR)
6896277e868Sjmcneill return;
6906277e868Sjmcneill if ((uvchdr->bmHeaderInfo & UV_PRES_TIME) == 0)
6916277e868Sjmcneill goto next;
6926277e868Sjmcneill
6936277e868Sjmcneill payload.data = buf + uvchdr->bHeaderLength;
694d1579b2dSriastradh payload.size = uimin(brem, len - uvchdr->bHeaderLength);
6956277e868Sjmcneill payload.frameno = UGETDW(&buf[2]);
6966277e868Sjmcneill payload.end_of_frame = uvchdr->bmHeaderInfo & UV_END_OF_FRAME;
6976277e868Sjmcneill video_submit_payload(sc->sc_videodev, &payload);
6986277e868Sjmcneill
6996277e868Sjmcneill next:
7006277e868Sjmcneill tlen -= len;
7016277e868Sjmcneill buf += len;
7026277e868Sjmcneill brem -= payload.size;
7036277e868Sjmcneill }
7046277e868Sjmcneill }
7056277e868Sjmcneill
7066277e868Sjmcneill static void
pseye_transfer_thread(void * opaque)7076902ca74Sjmcneill pseye_transfer_thread(void *opaque)
7086902ca74Sjmcneill {
7096902ca74Sjmcneill struct pseye_softc *sc = opaque;
7106277e868Sjmcneill uint32_t len;
7116902ca74Sjmcneill int error;
7126902ca74Sjmcneill
7136902ca74Sjmcneill while (sc->sc_running) {
7146277e868Sjmcneill len = sc->sc_bulkin_bufferlen;
7156277e868Sjmcneill error = pseye_get_frame(sc, &len);
7166277e868Sjmcneill if (error == USBD_NORMAL_COMPLETION)
7176277e868Sjmcneill pseye_submit_payload(sc, len);
7186902ca74Sjmcneill }
7196902ca74Sjmcneill
7206902ca74Sjmcneill mutex_enter(&sc->sc_mtx);
7216902ca74Sjmcneill cv_broadcast(&sc->sc_cv);
7226902ca74Sjmcneill mutex_exit(&sc->sc_mtx);
7236902ca74Sjmcneill
7246902ca74Sjmcneill kthread_exit(0);
7256902ca74Sjmcneill }
7266902ca74Sjmcneill
7276902ca74Sjmcneill /* video(9) API implementations */
7286902ca74Sjmcneill static int
pseye_open(void * opaque,int flags)7296902ca74Sjmcneill pseye_open(void *opaque, int flags)
7306902ca74Sjmcneill {
7316902ca74Sjmcneill struct pseye_softc *sc = opaque;
7326902ca74Sjmcneill
7336902ca74Sjmcneill if (sc->sc_dying)
7346902ca74Sjmcneill return EIO;
7356902ca74Sjmcneill
7364e8e6643Sskrll pseye_start(sc);
7374e8e6643Sskrll
7384e8e6643Sskrll return 0;
7396902ca74Sjmcneill }
7406902ca74Sjmcneill
7416902ca74Sjmcneill static void
pseye_close(void * opaque)7426902ca74Sjmcneill pseye_close(void *opaque)
7436902ca74Sjmcneill {
7446902ca74Sjmcneill struct pseye_softc *sc = opaque;
7456902ca74Sjmcneill
7464e8e6643Sskrll pseye_stop(sc);
7476902ca74Sjmcneill }
7486902ca74Sjmcneill
7496902ca74Sjmcneill static const char *
pseye_get_devname(void * opaque)7506902ca74Sjmcneill pseye_get_devname(void *opaque)
7516902ca74Sjmcneill {
75239177e11Sjmcneill return "PlayStation Eye";
7536902ca74Sjmcneill }
7546902ca74Sjmcneill
755df0c38f6Sjmcneill static const char *
pseye_get_businfo(void * opaque)756df0c38f6Sjmcneill pseye_get_businfo(void *opaque)
757df0c38f6Sjmcneill {
758df0c38f6Sjmcneill struct pseye_softc *sc = opaque;
759df0c38f6Sjmcneill
760df0c38f6Sjmcneill return sc->sc_businfo;
761df0c38f6Sjmcneill }
762df0c38f6Sjmcneill
7636902ca74Sjmcneill static int
pseye_enum_format(void * opaque,uint32_t index,struct video_format * format)76494e5d88fSjmcneill pseye_enum_format(void *opaque, uint32_t index, struct video_format *format)
76594e5d88fSjmcneill {
76694e5d88fSjmcneill if (index != 0)
76794e5d88fSjmcneill return EINVAL;
76894e5d88fSjmcneill return pseye_get_format(opaque, format);
76994e5d88fSjmcneill }
77094e5d88fSjmcneill
77194e5d88fSjmcneill static int
pseye_get_format(void * opaque,struct video_format * format)7726902ca74Sjmcneill pseye_get_format(void *opaque, struct video_format *format)
7736902ca74Sjmcneill {
7746902ca74Sjmcneill format->pixel_format = VIDEO_FORMAT_YUY2; /* XXX actually YUYV */
7756902ca74Sjmcneill format->width = 640;
7766902ca74Sjmcneill format->height = 480;
7776902ca74Sjmcneill format->aspect_x = 4;
7786902ca74Sjmcneill format->aspect_y = 3;
7796902ca74Sjmcneill format->sample_size = format->width * format->height * 2;
7806902ca74Sjmcneill format->stride = format->width * 2;
7816902ca74Sjmcneill format->color.primaries = VIDEO_COLOR_PRIMARIES_UNSPECIFIED;
7826902ca74Sjmcneill format->color.gamma_function = VIDEO_GAMMA_FUNCTION_UNSPECIFIED;
7836902ca74Sjmcneill format->color.matrix_coeff = VIDEO_MATRIX_COEFF_UNSPECIFIED;
7846902ca74Sjmcneill format->interlace_flags = VIDEO_INTERLACE_ON;
7856902ca74Sjmcneill format->priv = 0;
7866902ca74Sjmcneill
7876902ca74Sjmcneill return 0;
7886902ca74Sjmcneill }
7896902ca74Sjmcneill
7906902ca74Sjmcneill static int
pseye_set_format(void * opaque,struct video_format * format)7916902ca74Sjmcneill pseye_set_format(void *opaque, struct video_format *format)
7926902ca74Sjmcneill {
7936902ca74Sjmcneill #if notyet
7946902ca74Sjmcneill if (format->pixel_format != VIDEO_FORMAT_YUYV)
7956902ca74Sjmcneill return EINVAL;
7966902ca74Sjmcneill if (format->width != 640 || format->height != 480)
7976902ca74Sjmcneill return EINVAL;
7986902ca74Sjmcneill #endif
7996902ca74Sjmcneill /* XXX */
8006902ca74Sjmcneill return pseye_get_format(opaque, format);
8016902ca74Sjmcneill }
8026902ca74Sjmcneill
8036902ca74Sjmcneill static int
pseye_try_format(void * opaque,struct video_format * format)804dd848685Sjmcneill pseye_try_format(void *opaque, struct video_format *format)
805dd848685Sjmcneill {
806dd848685Sjmcneill return pseye_get_format(opaque, format);
807dd848685Sjmcneill }
808dd848685Sjmcneill
809dd848685Sjmcneill static int
pseye_get_framerate(void * opaque,struct video_fract * fract)810bb520deeSjmcneill pseye_get_framerate(void *opaque, struct video_fract *fract)
811bb520deeSjmcneill {
812bb520deeSjmcneill /* Driver only supports 60fps */
813bb520deeSjmcneill fract->numerator = 1;
814bb520deeSjmcneill fract->denominator = 60;
815bb520deeSjmcneill
816bb520deeSjmcneill return 0;
817bb520deeSjmcneill }
818bb520deeSjmcneill
819bb520deeSjmcneill static int
pseye_set_framerate(void * opaque,struct video_fract * fract)820bb520deeSjmcneill pseye_set_framerate(void *opaque, struct video_fract *fract)
821bb520deeSjmcneill {
822bb520deeSjmcneill /* Driver only supports one framerate. Return actual rate. */
823bb520deeSjmcneill return pseye_get_framerate(opaque, fract);
824bb520deeSjmcneill }
825bb520deeSjmcneill
826bb520deeSjmcneill static int
pseye_start_transfer(void * opaque)8276902ca74Sjmcneill pseye_start_transfer(void *opaque)
8286902ca74Sjmcneill {
8296902ca74Sjmcneill struct pseye_softc *sc = opaque;
8306902ca74Sjmcneill int err = 0;
8316902ca74Sjmcneill
8326902ca74Sjmcneill mutex_enter(&sc->sc_mtx);
8336902ca74Sjmcneill if (sc->sc_running == 0) {
8346902ca74Sjmcneill sc->sc_running = 1;
8356ea027b8Sjmcneill err = kthread_create(PRI_PSEYE, 0, NULL, pseye_transfer_thread,
836c79f3b8aSjoerg opaque, NULL, "%s", device_xname(sc->sc_dev));
8376902ca74Sjmcneill } else
8386902ca74Sjmcneill aprint_error_dev(sc->sc_dev, "transfer already in progress\n");
8396902ca74Sjmcneill mutex_exit(&sc->sc_mtx);
8406902ca74Sjmcneill
8410074b883Smjf return err;
8426902ca74Sjmcneill }
8436902ca74Sjmcneill
8446902ca74Sjmcneill static int
pseye_stop_transfer(void * opaque)8456902ca74Sjmcneill pseye_stop_transfer(void *opaque)
8466902ca74Sjmcneill {
8476902ca74Sjmcneill struct pseye_softc *sc = opaque;
8486902ca74Sjmcneill
8496902ca74Sjmcneill mutex_enter(&sc->sc_mtx);
8506902ca74Sjmcneill if (sc->sc_running) {
8516902ca74Sjmcneill sc->sc_running = 0;
8526902ca74Sjmcneill cv_wait_sig(&sc->sc_cv, &sc->sc_mtx);
8536902ca74Sjmcneill }
8546902ca74Sjmcneill mutex_exit(&sc->sc_mtx);
8556902ca74Sjmcneill
8566902ca74Sjmcneill return 0;
8576902ca74Sjmcneill }
8585a635bb2Sjmcneill
8595a635bb2Sjmcneill MODULE(MODULE_CLASS_DRIVER, pseye, NULL);
8605a635bb2Sjmcneill
861201870c3Sjmcneill #ifdef _MODULE
862201870c3Sjmcneill #include "ioconf.c"
863201870c3Sjmcneill #endif
8645a635bb2Sjmcneill
8655a635bb2Sjmcneill static int
pseye_modcmd(modcmd_t cmd,void * opaque)8665a635bb2Sjmcneill pseye_modcmd(modcmd_t cmd, void *opaque)
8675a635bb2Sjmcneill {
8685a635bb2Sjmcneill switch (cmd) {
8695a635bb2Sjmcneill case MODULE_CMD_INIT:
870201870c3Sjmcneill #ifdef _MODULE
871201870c3Sjmcneill return config_init_component(cfdriver_ioconf_pseye,
872201870c3Sjmcneill cfattach_ioconf_pseye, cfdata_ioconf_pseye);
873201870c3Sjmcneill #else
8745a635bb2Sjmcneill return 0;
875201870c3Sjmcneill #endif
8765a635bb2Sjmcneill case MODULE_CMD_FINI:
877201870c3Sjmcneill #ifdef _MODULE
878201870c3Sjmcneill return config_fini_component(cfdriver_ioconf_pseye,
879201870c3Sjmcneill cfattach_ioconf_pseye, cfdata_ioconf_pseye);
880201870c3Sjmcneill #else
8815a635bb2Sjmcneill return 0;
882201870c3Sjmcneill #endif
8835a635bb2Sjmcneill default:
8845a635bb2Sjmcneill return ENOTTY;
8855a635bb2Sjmcneill }
8865a635bb2Sjmcneill }
887