| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * uvc_video.c -- USB Video Class driver - Video handling |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2005-2010 |
|---|
| 5 | 6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | | - * (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | 7 | */ |
|---|
| 13 | 8 | |
|---|
| 14 | 9 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 25 | 20 | #include <media/v4l2-common.h> |
|---|
| 26 | 21 | |
|---|
| 27 | 22 | #include "uvcvideo.h" |
|---|
| 23 | +#include <soc/rockchip/rockchip-system-status.h> |
|---|
| 28 | 24 | |
|---|
| 29 | 25 | /* ------------------------------------------------------------------------ |
|---|
| 30 | 26 | * UVC Controls |
|---|
| .. | .. |
|---|
| 659 | 655 | * to avoid losing precision in the division. Similarly, the host timestamp is |
|---|
| 660 | 656 | * computed with |
|---|
| 661 | 657 | * |
|---|
| 662 | | - * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) |
|---|
| 658 | + * TS = ((TS2 - TS1) * SOF + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) |
|---|
| 663 | 659 | * |
|---|
| 664 | 660 | * SOF values are coded on 11 bits by USB. We extend their precision with 16 |
|---|
| 665 | 661 | * decimal bits, leading to a 11.16 coding. |
|---|
| .. | .. |
|---|
| 802 | 798 | unsigned int header_size; |
|---|
| 803 | 799 | bool has_pts = false; |
|---|
| 804 | 800 | bool has_scr = false; |
|---|
| 805 | | - u16 uninitialized_var(scr_sof); |
|---|
| 806 | | - u32 uninitialized_var(scr_stc); |
|---|
| 807 | | - u32 uninitialized_var(pts); |
|---|
| 801 | + u16 scr_sof; |
|---|
| 802 | + u32 scr_stc; |
|---|
| 803 | + u32 pts; |
|---|
| 808 | 804 | |
|---|
| 809 | 805 | if (stream->stats.stream.nb_frames == 0 && |
|---|
| 810 | 806 | stream->stats.frame.nb_packets == 0) |
|---|
| .. | .. |
|---|
| 1313 | 1309 | if (has_scr) |
|---|
| 1314 | 1310 | memcpy(stream->clock.last_scr, scr, 6); |
|---|
| 1315 | 1311 | |
|---|
| 1316 | | - memcpy(&meta->length, mem, length); |
|---|
| 1312 | + meta->length = mem[0]; |
|---|
| 1313 | + meta->flags = mem[1]; |
|---|
| 1314 | + memcpy(meta->buf, &mem[2], length - 2); |
|---|
| 1317 | 1315 | meta_buf->bytesused += length + sizeof(meta->ns) + sizeof(meta->sof); |
|---|
| 1318 | 1316 | |
|---|
| 1319 | 1317 | uvc_trace(UVC_TRACE_FRAME, |
|---|
| .. | .. |
|---|
| 1546 | 1544 | default: |
|---|
| 1547 | 1545 | uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " |
|---|
| 1548 | 1546 | "completion handler.\n", urb->status); |
|---|
| 1549 | | - /* fall through */ |
|---|
| 1547 | + fallthrough; |
|---|
| 1550 | 1548 | case -ENOENT: /* usb_poison_urb() called. */ |
|---|
| 1551 | 1549 | if (stream->frozen) |
|---|
| 1552 | 1550 | return; |
|---|
| 1553 | | - /* fall through */ |
|---|
| 1551 | + fallthrough; |
|---|
| 1554 | 1552 | case -ECONNRESET: /* usb_unlink_urb() called. */ |
|---|
| 1555 | 1553 | case -ESHUTDOWN: /* The endpoint is being disabled. */ |
|---|
| 1556 | 1554 | uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); |
|---|
| .. | .. |
|---|
| 1596 | 1594 | */ |
|---|
| 1597 | 1595 | static void uvc_free_urb_buffers(struct uvc_streaming *stream) |
|---|
| 1598 | 1596 | { |
|---|
| 1599 | | - unsigned int i; |
|---|
| 1597 | + struct uvc_urb *uvc_urb; |
|---|
| 1600 | 1598 | |
|---|
| 1601 | | - for (i = 0; i < UVC_URBS; ++i) { |
|---|
| 1602 | | - struct uvc_urb *uvc_urb = &stream->uvc_urb[i]; |
|---|
| 1599 | + for_each_uvc_urb(uvc_urb, stream) { |
|---|
| 1600 | + if (!uvc_urb->buffer) |
|---|
| 1601 | + continue; |
|---|
| 1603 | 1602 | |
|---|
| 1604 | | - if (uvc_urb->buffer) { |
|---|
| 1605 | 1603 | #ifndef CONFIG_DMA_NONCOHERENT |
|---|
| 1606 | | - usb_free_coherent(stream->dev->udev, stream->urb_size, |
|---|
| 1607 | | - uvc_urb->buffer, uvc_urb->dma); |
|---|
| 1604 | + usb_free_coherent(stream->dev->udev, stream->urb_size, |
|---|
| 1605 | + uvc_urb->buffer, uvc_urb->dma); |
|---|
| 1608 | 1606 | #else |
|---|
| 1609 | | - kfree(uvc_urb->buffer); |
|---|
| 1607 | + kfree(uvc_urb->buffer); |
|---|
| 1610 | 1608 | #endif |
|---|
| 1611 | | - uvc_urb->buffer = NULL; |
|---|
| 1612 | | - } |
|---|
| 1609 | + uvc_urb->buffer = NULL; |
|---|
| 1613 | 1610 | } |
|---|
| 1614 | 1611 | |
|---|
| 1615 | 1612 | stream->urb_size = 0; |
|---|
| .. | .. |
|---|
| 1681 | 1678 | /* |
|---|
| 1682 | 1679 | * Uninitialize isochronous/bulk URBs and free transfer buffers. |
|---|
| 1683 | 1680 | */ |
|---|
| 1684 | | -static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) |
|---|
| 1681 | +static void uvc_video_stop_transfer(struct uvc_streaming *stream, |
|---|
| 1682 | + int free_buffers) |
|---|
| 1685 | 1683 | { |
|---|
| 1686 | 1684 | struct uvc_urb *uvc_urb; |
|---|
| 1687 | 1685 | |
|---|
| .. | .. |
|---|
| 1740 | 1738 | struct usb_host_endpoint *ep, gfp_t gfp_flags) |
|---|
| 1741 | 1739 | { |
|---|
| 1742 | 1740 | struct urb *urb; |
|---|
| 1743 | | - unsigned int npackets, i, j; |
|---|
| 1741 | + struct uvc_urb *uvc_urb; |
|---|
| 1742 | + unsigned int npackets, i; |
|---|
| 1744 | 1743 | u16 psize; |
|---|
| 1745 | 1744 | u32 size; |
|---|
| 1746 | 1745 | |
|---|
| .. | .. |
|---|
| 1753 | 1752 | |
|---|
| 1754 | 1753 | size = npackets * psize; |
|---|
| 1755 | 1754 | |
|---|
| 1756 | | - for (i = 0; i < UVC_URBS; ++i) { |
|---|
| 1757 | | - struct uvc_urb *uvc_urb = &stream->uvc_urb[i]; |
|---|
| 1758 | | - |
|---|
| 1755 | + for_each_uvc_urb(uvc_urb, stream) { |
|---|
| 1759 | 1756 | urb = usb_alloc_urb(npackets, gfp_flags); |
|---|
| 1760 | 1757 | if (urb == NULL) { |
|---|
| 1761 | | - uvc_uninit_video(stream, 1); |
|---|
| 1758 | + uvc_video_stop_transfer(stream, 1); |
|---|
| 1762 | 1759 | return -ENOMEM; |
|---|
| 1763 | 1760 | } |
|---|
| 1764 | 1761 | |
|---|
| .. | .. |
|---|
| 1778 | 1775 | urb->number_of_packets = npackets; |
|---|
| 1779 | 1776 | urb->transfer_buffer_length = size; |
|---|
| 1780 | 1777 | |
|---|
| 1781 | | - for (j = 0; j < npackets; ++j) { |
|---|
| 1782 | | - urb->iso_frame_desc[j].offset = j * psize; |
|---|
| 1783 | | - urb->iso_frame_desc[j].length = psize; |
|---|
| 1778 | + for (i = 0; i < npackets; ++i) { |
|---|
| 1779 | + urb->iso_frame_desc[i].offset = i * psize; |
|---|
| 1780 | + urb->iso_frame_desc[i].length = psize; |
|---|
| 1784 | 1781 | } |
|---|
| 1785 | 1782 | |
|---|
| 1786 | 1783 | uvc_urb->urb = urb; |
|---|
| .. | .. |
|---|
| 1797 | 1794 | struct usb_host_endpoint *ep, gfp_t gfp_flags) |
|---|
| 1798 | 1795 | { |
|---|
| 1799 | 1796 | struct urb *urb; |
|---|
| 1800 | | - unsigned int npackets, pipe, i; |
|---|
| 1797 | + struct uvc_urb *uvc_urb; |
|---|
| 1798 | + unsigned int npackets, pipe; |
|---|
| 1801 | 1799 | u16 psize; |
|---|
| 1802 | 1800 | u32 size; |
|---|
| 1803 | 1801 | |
|---|
| .. | .. |
|---|
| 1821 | 1819 | if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) |
|---|
| 1822 | 1820 | size = 0; |
|---|
| 1823 | 1821 | |
|---|
| 1824 | | - for (i = 0; i < UVC_URBS; ++i) { |
|---|
| 1825 | | - struct uvc_urb *uvc_urb = &stream->uvc_urb[i]; |
|---|
| 1826 | | - |
|---|
| 1822 | + for_each_uvc_urb(uvc_urb, stream) { |
|---|
| 1827 | 1823 | urb = usb_alloc_urb(0, gfp_flags); |
|---|
| 1828 | 1824 | if (urb == NULL) { |
|---|
| 1829 | | - uvc_uninit_video(stream, 1); |
|---|
| 1825 | + uvc_video_stop_transfer(stream, 1); |
|---|
| 1830 | 1826 | return -ENOMEM; |
|---|
| 1831 | 1827 | } |
|---|
| 1832 | 1828 | |
|---|
| .. | .. |
|---|
| 1846 | 1842 | /* |
|---|
| 1847 | 1843 | * Initialize isochronous/bulk URBs and allocate transfer buffers. |
|---|
| 1848 | 1844 | */ |
|---|
| 1849 | | -static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) |
|---|
| 1845 | +static int uvc_video_start_transfer(struct uvc_streaming *stream, |
|---|
| 1846 | + gfp_t gfp_flags) |
|---|
| 1850 | 1847 | { |
|---|
| 1851 | 1848 | struct usb_interface *intf = stream->intf; |
|---|
| 1852 | 1849 | struct usb_host_endpoint *ep; |
|---|
| 1850 | + struct uvc_urb *uvc_urb; |
|---|
| 1853 | 1851 | unsigned int i; |
|---|
| 1854 | 1852 | int ret; |
|---|
| 1855 | 1853 | |
|---|
| .. | .. |
|---|
| 1865 | 1863 | struct usb_host_endpoint *best_ep = NULL; |
|---|
| 1866 | 1864 | unsigned int best_psize = UINT_MAX; |
|---|
| 1867 | 1865 | unsigned int bandwidth; |
|---|
| 1868 | | - unsigned int uninitialized_var(altsetting); |
|---|
| 1866 | + unsigned int altsetting; |
|---|
| 1869 | 1867 | int intfnum = stream->intfnum; |
|---|
| 1870 | 1868 | |
|---|
| 1871 | 1869 | /* Isochronous endpoint, select the alternate setting. */ |
|---|
| .. | .. |
|---|
| 1908 | 1906 | uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u " |
|---|
| 1909 | 1907 | "(%u B/frame bandwidth).\n", altsetting, best_psize); |
|---|
| 1910 | 1908 | |
|---|
| 1909 | + /* |
|---|
| 1910 | + * Some devices, namely the Logitech C910 and B910, are unable |
|---|
| 1911 | + * to recover from a USB autosuspend, unless the alternate |
|---|
| 1912 | + * setting of the streaming interface is toggled. |
|---|
| 1913 | + */ |
|---|
| 1914 | + if (stream->dev->quirks & UVC_QUIRK_WAKE_AUTOSUSPEND) { |
|---|
| 1915 | + usb_set_interface(stream->dev->udev, intfnum, |
|---|
| 1916 | + altsetting); |
|---|
| 1917 | + usb_set_interface(stream->dev->udev, intfnum, 0); |
|---|
| 1918 | + } |
|---|
| 1919 | + |
|---|
| 1911 | 1920 | ret = usb_set_interface(stream->dev->udev, intfnum, altsetting); |
|---|
| 1912 | 1921 | if (ret < 0) |
|---|
| 1913 | 1922 | return ret; |
|---|
| .. | .. |
|---|
| 1931 | 1940 | return ret; |
|---|
| 1932 | 1941 | |
|---|
| 1933 | 1942 | /* Submit the URBs. */ |
|---|
| 1934 | | - for (i = 0; i < UVC_URBS; ++i) { |
|---|
| 1935 | | - struct uvc_urb *uvc_urb = &stream->uvc_urb[i]; |
|---|
| 1936 | | - |
|---|
| 1943 | + for_each_uvc_urb(uvc_urb, stream) { |
|---|
| 1937 | 1944 | ret = usb_submit_urb(uvc_urb->urb, gfp_flags); |
|---|
| 1938 | 1945 | if (ret < 0) { |
|---|
| 1939 | | - uvc_printk(KERN_ERR, "Failed to submit URB %u " |
|---|
| 1940 | | - "(%d).\n", i, ret); |
|---|
| 1941 | | - uvc_uninit_video(stream, 1); |
|---|
| 1946 | + uvc_printk(KERN_ERR, "Failed to submit URB %u (%d).\n", |
|---|
| 1947 | + uvc_urb_index(uvc_urb), ret); |
|---|
| 1948 | + uvc_video_stop_transfer(stream, 1); |
|---|
| 1942 | 1949 | return ret; |
|---|
| 1943 | 1950 | } |
|---|
| 1944 | 1951 | } |
|---|
| .. | .. |
|---|
| 1969 | 1976 | return 0; |
|---|
| 1970 | 1977 | |
|---|
| 1971 | 1978 | stream->frozen = 1; |
|---|
| 1972 | | - uvc_uninit_video(stream, 0); |
|---|
| 1979 | + uvc_video_stop_transfer(stream, 0); |
|---|
| 1973 | 1980 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); |
|---|
| 1974 | 1981 | return 0; |
|---|
| 1975 | 1982 | } |
|---|
| .. | .. |
|---|
| 2005 | 2012 | if (ret < 0) |
|---|
| 2006 | 2013 | return ret; |
|---|
| 2007 | 2014 | |
|---|
| 2008 | | - return uvc_init_video(stream, GFP_NOIO); |
|---|
| 2015 | + return uvc_video_start_transfer(stream, GFP_NOIO); |
|---|
| 2009 | 2016 | } |
|---|
| 2010 | 2017 | |
|---|
| 2011 | 2018 | /* ------------------------------------------------------------------------ |
|---|
| .. | .. |
|---|
| 2046 | 2053 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); |
|---|
| 2047 | 2054 | |
|---|
| 2048 | 2055 | /* Set the streaming probe control with default streaming parameters |
|---|
| 2049 | | - * retrieved from the device. Webcams that don't suport GET_DEF |
|---|
| 2056 | + * retrieved from the device. Webcams that don't support GET_DEF |
|---|
| 2050 | 2057 | * requests on the probe control will just keep their current streaming |
|---|
| 2051 | 2058 | * parameters. |
|---|
| 2052 | 2059 | */ |
|---|
| .. | .. |
|---|
| 2120 | 2127 | return 0; |
|---|
| 2121 | 2128 | } |
|---|
| 2122 | 2129 | |
|---|
| 2123 | | -/* |
|---|
| 2124 | | - * Enable or disable the video stream. |
|---|
| 2125 | | - */ |
|---|
| 2126 | | -int uvc_video_enable(struct uvc_streaming *stream, int enable) |
|---|
| 2130 | +int uvc_video_start_streaming(struct uvc_streaming *stream) |
|---|
| 2127 | 2131 | { |
|---|
| 2128 | 2132 | int ret; |
|---|
| 2129 | | - |
|---|
| 2130 | | - if (!enable) { |
|---|
| 2131 | | - uvc_uninit_video(stream, 1); |
|---|
| 2132 | | - if (stream->intf->num_altsetting > 1) { |
|---|
| 2133 | | - usb_set_interface(stream->dev->udev, |
|---|
| 2134 | | - stream->intfnum, 0); |
|---|
| 2135 | | - } else { |
|---|
| 2136 | | - /* UVC doesn't specify how to inform a bulk-based device |
|---|
| 2137 | | - * when the video stream is stopped. Windows sends a |
|---|
| 2138 | | - * CLEAR_FEATURE(HALT) request to the video streaming |
|---|
| 2139 | | - * bulk endpoint, mimic the same behaviour. |
|---|
| 2140 | | - */ |
|---|
| 2141 | | - unsigned int epnum = stream->header.bEndpointAddress |
|---|
| 2142 | | - & USB_ENDPOINT_NUMBER_MASK; |
|---|
| 2143 | | - unsigned int dir = stream->header.bEndpointAddress |
|---|
| 2144 | | - & USB_ENDPOINT_DIR_MASK; |
|---|
| 2145 | | - unsigned int pipe; |
|---|
| 2146 | | - |
|---|
| 2147 | | - pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir; |
|---|
| 2148 | | - usb_clear_halt(stream->dev->udev, pipe); |
|---|
| 2149 | | - } |
|---|
| 2150 | | - |
|---|
| 2151 | | - uvc_video_clock_cleanup(stream); |
|---|
| 2152 | | - return 0; |
|---|
| 2153 | | - } |
|---|
| 2154 | 2133 | |
|---|
| 2155 | 2134 | ret = uvc_video_clock_init(stream); |
|---|
| 2156 | 2135 | if (ret < 0) |
|---|
| .. | .. |
|---|
| 2161 | 2140 | if (ret < 0) |
|---|
| 2162 | 2141 | goto error_commit; |
|---|
| 2163 | 2142 | |
|---|
| 2164 | | - ret = uvc_init_video(stream, GFP_KERNEL); |
|---|
| 2143 | + rockchip_set_system_status(SYS_STATUS_PERFORMANCE); |
|---|
| 2144 | + |
|---|
| 2145 | + ret = uvc_video_start_transfer(stream, GFP_KERNEL); |
|---|
| 2165 | 2146 | if (ret < 0) |
|---|
| 2166 | 2147 | goto error_video; |
|---|
| 2167 | 2148 | |
|---|
| 2168 | 2149 | return 0; |
|---|
| 2169 | 2150 | |
|---|
| 2170 | 2151 | error_video: |
|---|
| 2152 | + rockchip_clear_system_status(SYS_STATUS_PERFORMANCE); |
|---|
| 2171 | 2153 | usb_set_interface(stream->dev->udev, stream->intfnum, 0); |
|---|
| 2172 | 2154 | error_commit: |
|---|
| 2173 | 2155 | uvc_video_clock_cleanup(stream); |
|---|
| 2174 | 2156 | |
|---|
| 2175 | 2157 | return ret; |
|---|
| 2176 | 2158 | } |
|---|
| 2159 | + |
|---|
| 2160 | +void uvc_video_stop_streaming(struct uvc_streaming *stream) |
|---|
| 2161 | +{ |
|---|
| 2162 | + uvc_video_stop_transfer(stream, 1); |
|---|
| 2163 | + |
|---|
| 2164 | + if (stream->intf->num_altsetting > 1) { |
|---|
| 2165 | + usb_set_interface(stream->dev->udev, stream->intfnum, 0); |
|---|
| 2166 | + } else { |
|---|
| 2167 | + /* UVC doesn't specify how to inform a bulk-based device |
|---|
| 2168 | + * when the video stream is stopped. Windows sends a |
|---|
| 2169 | + * CLEAR_FEATURE(HALT) request to the video streaming |
|---|
| 2170 | + * bulk endpoint, mimic the same behaviour. |
|---|
| 2171 | + */ |
|---|
| 2172 | + unsigned int epnum = stream->header.bEndpointAddress |
|---|
| 2173 | + & USB_ENDPOINT_NUMBER_MASK; |
|---|
| 2174 | + unsigned int dir = stream->header.bEndpointAddress |
|---|
| 2175 | + & USB_ENDPOINT_DIR_MASK; |
|---|
| 2176 | + unsigned int pipe; |
|---|
| 2177 | + |
|---|
| 2178 | + pipe = usb_sndbulkpipe(stream->dev->udev, epnum) | dir; |
|---|
| 2179 | + usb_clear_halt(stream->dev->udev, pipe); |
|---|
| 2180 | + } |
|---|
| 2181 | + |
|---|
| 2182 | + uvc_video_clock_cleanup(stream); |
|---|
| 2183 | + rockchip_clear_system_status(SYS_STATUS_PERFORMANCE); |
|---|
| 2184 | +} |
|---|