.. | .. |
---|
| 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 | +} |
---|