mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-24 11:16:51 +00:00
linux: remove kernel 3.12 patches and support
Signed-off-by: Stephan Raue <stephan@openelec.tv>
This commit is contained in:
parent
0ead3a8012
commit
41ce501ddb
@ -18,10 +18,6 @@
|
||||
|
||||
PKG_NAME="linux"
|
||||
case "$LINUX" in
|
||||
3.12)
|
||||
PKG_VERSION="3.12.8"
|
||||
PKG_URL="http://www.kernel.org/pub/linux/kernel/v3.x/$PKG_NAME-$PKG_VERSION.tar.xz"
|
||||
;;
|
||||
imx6)
|
||||
PKG_VERSION="imx_3.0.35_4.1.0-0f2006c"
|
||||
PKG_URL="$DISTRO_SRC/$PKG_NAME-$PKG_VERSION.tar.xz"
|
||||
|
@ -1,21 +0,0 @@
|
||||
diff --git a/init/main.c b/init/main.c
|
||||
index 9484f4b..db55edd 100644
|
||||
--- a/init/main.c
|
||||
+++ b/init/main.c
|
||||
@@ -880,8 +880,14 @@ static noinline void __init kernel_init_freeable(void)
|
||||
do_basic_setup();
|
||||
|
||||
/* Open the /dev/console on the rootfs, this should never fail */
|
||||
- if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
|
||||
- pr_err("Warning: unable to open an initial console.\n");
|
||||
+ char *console = "/dev_console";
|
||||
+
|
||||
+ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) {
|
||||
+ sys_mknod(console, S_IFCHR|0600, (TTYAUX_MAJOR<<8)|1);
|
||||
+ if (sys_open(console, O_RDWR, 0) < 0)
|
||||
+ printk(KERN_WARNING "Warning: unable to open an initial console.\n");
|
||||
+ sys_unlink(console);
|
||||
+ }
|
||||
|
||||
(void) sys_dup(0);
|
||||
(void) sys_dup(0);
|
File diff suppressed because it is too large
Load Diff
@ -1,161 +0,0 @@
|
||||
diff -Naur linux-3.9/drivers/hid/hid-core.c linux-3.9.patch/drivers/hid/hid-core.c
|
||||
--- linux-3.9/drivers/hid/hid-core.c 2013-04-29 02:36:01.000000000 +0200
|
||||
+++ linux-3.9.patch/drivers/hid/hid-core.c 2013-04-29 17:08:40.528324010 +0200
|
||||
@@ -1681,6 +1681,9 @@
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1) },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2) },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
|
||||
#if IS_ENABLED(CONFIG_HID_ROCCAT)
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
|
||||
diff -Naur linux-3.9/drivers/hid/hid-ids.h linux-3.9.patch/drivers/hid/hid-ids.h
|
||||
--- linux-3.9/drivers/hid/hid-ids.h 2013-04-29 02:36:01.000000000 +0200
|
||||
+++ linux-3.9.patch/drivers/hid/hid-ids.h 2013-04-29 17:08:40.537323981 +0200
|
||||
@@ -663,6 +663,9 @@
|
||||
|
||||
#define USB_VENDOR_ID_PHILIPS 0x0471
|
||||
#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
|
||||
+#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1 0x206c
|
||||
+#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2 0x20cc
|
||||
+#define USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3 0x0613
|
||||
|
||||
#define USB_VENDOR_ID_PI_ENGINEERING 0x05f3
|
||||
#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
|
||||
diff -Naur linux-3.9/drivers/hid/hid-spinelplus.c linux-3.9.patch/drivers/hid/hid-spinelplus.c
|
||||
--- linux-3.9/drivers/hid/hid-spinelplus.c 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ linux-3.9.patch/drivers/hid/hid-spinelplus.c 2013-04-29 17:08:40.537323981 +0200
|
||||
@@ -0,0 +1,104 @@
|
||||
+/*
|
||||
+ * HID driver for "PHILIPS MCE USB IR Receiver- Spinel plus" remotes
|
||||
+ *
|
||||
+ * Copyright (c) 2010 Panagiotis Skintzos
|
||||
+ *
|
||||
+ * Renamed to Spinel, cleanup and modified to also support
|
||||
+ * Spinel Plus 0471:20CC by Stephan Raue 2012.
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License as published by the Free
|
||||
+ * Software Foundation; either version 2 of the License, or (at your option)
|
||||
+ * any later version.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/input.h>
|
||||
+#include <linux/hid.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include "hid-ids.h"
|
||||
+
|
||||
+#define spinelplus_map_key(c) set_bit(EV_REP, hi->input->evbit); \
|
||||
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||
+
|
||||
+static int spinelplus_input_mapping(struct hid_device *hdev,
|
||||
+ struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
|
||||
+ unsigned long **bit, int *max)
|
||||
+{
|
||||
+ switch (usage->hid) {
|
||||
+ case 0xffbc000d: spinelplus_map_key(KEY_MEDIA); break;
|
||||
+ case 0xffbc0024: spinelplus_map_key(KEY_MEDIA); break;
|
||||
+ case 0xffbc0027: spinelplus_map_key(KEY_ZOOM); break;
|
||||
+ case 0xffbc0033: spinelplus_map_key(KEY_HOME); break;
|
||||
+ case 0xffbc0035: spinelplus_map_key(KEY_CAMERA); break;
|
||||
+ case 0xffbc0036: spinelplus_map_key(KEY_EPG); break;
|
||||
+ case 0xffbc0037: spinelplus_map_key(KEY_DVD); break;
|
||||
+ case 0xffbc0038: spinelplus_map_key(KEY_HOME); break;
|
||||
+ case 0xffbc0039: spinelplus_map_key(KEY_MP3); break;
|
||||
+ case 0xffbc003a: spinelplus_map_key(KEY_VIDEO); break;
|
||||
+ case 0xffbc005a: spinelplus_map_key(KEY_TEXT); break;
|
||||
+ case 0xffbc005b: spinelplus_map_key(KEY_RED); break;
|
||||
+ case 0xffbc005c: spinelplus_map_key(KEY_GREEN); break;
|
||||
+ case 0xffbc005d: spinelplus_map_key(KEY_YELLOW); break;
|
||||
+ case 0xffbc005e: spinelplus_map_key(KEY_BLUE); break;
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static int spinelplus_probe(struct hid_device *hdev,
|
||||
+ const struct hid_device_id *id)
|
||||
+{
|
||||
+ int ret;
|
||||
+ /* Connect only to hid input (not hiddev & hidraw)*/
|
||||
+ unsigned int cmask = HID_CONNECT_HIDINPUT;
|
||||
+
|
||||
+ ret = hid_parse(hdev);
|
||||
+ if (ret) {
|
||||
+ dev_err(&hdev->dev, "parse failed\n");
|
||||
+ goto err_free;
|
||||
+ }
|
||||
+
|
||||
+ ret = hid_hw_start(hdev, cmask);
|
||||
+ if (ret) {
|
||||
+ dev_err(&hdev->dev, "hw start failed\n");
|
||||
+ goto err_free;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+err_free:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static const struct hid_device_id spinelplus_devices[] = {
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_1) },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_2) },
|
||||
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS,USB_DEVICE_ID_PHILIPS_SPINEL_PLUS_3) },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(hid, spinelplus_devices);
|
||||
+
|
||||
+static struct hid_driver spinelplus_driver = {
|
||||
+ .name = "SpinelPlus",
|
||||
+ .id_table = spinelplus_devices,
|
||||
+ .input_mapping = spinelplus_input_mapping,
|
||||
+ .probe = spinelplus_probe,
|
||||
+};
|
||||
+
|
||||
+static int __init spinelplus_init(void)
|
||||
+{
|
||||
+ return hid_register_driver(&spinelplus_driver);
|
||||
+}
|
||||
+
|
||||
+static void __exit spinelplus_exit(void)
|
||||
+{
|
||||
+ hid_unregister_driver(&spinelplus_driver);
|
||||
+}
|
||||
+
|
||||
+module_init(spinelplus_init);
|
||||
+module_exit(spinelplus_exit);
|
||||
+MODULE_LICENSE("GPL");
|
||||
diff -Naur linux-3.9/drivers/hid/Kconfig linux-3.9.patch/drivers/hid/Kconfig
|
||||
--- linux-3.9/drivers/hid/Kconfig 2013-04-29 02:36:01.000000000 +0200
|
||||
+++ linux-3.9.patch/drivers/hid/Kconfig 2013-04-29 17:08:40.538323977 +0200
|
||||
@@ -602,6 +602,12 @@
|
||||
---help---
|
||||
Support for Steelseries SRW-S1 steering wheel
|
||||
|
||||
+config HID_SPINELPLUS
|
||||
+ tristate "Spinel Plus remote control"
|
||||
+ depends on USB_HID
|
||||
+ ---help---
|
||||
+ Say Y here if you have a Spinel Plus (0471:206c/20cc/0613) remote
|
||||
+
|
||||
config HID_SUNPLUS
|
||||
tristate "Sunplus wireless desktop"
|
||||
depends on USB_HID
|
||||
diff -Naur linux-3.9/drivers/hid/Makefile linux-3.9.patch/drivers/hid/Makefile
|
||||
--- linux-3.9/drivers/hid/Makefile 2013-04-29 02:36:01.000000000 +0200
|
||||
+++ linux-3.9.patch/drivers/hid/Makefile 2013-04-29 17:09:26.744173841 +0200
|
||||
@@ -101,6 +101,7 @@
|
||||
obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
|
||||
obj-$(CONFIG_HID_SONY) += hid-sony.o
|
||||
obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
|
||||
+obj-$(CONFIG_HID_SPINELPLUS) += hid-spinelplus.o
|
||||
obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o
|
||||
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
|
||||
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
|
@ -1,12 +0,0 @@
|
||||
diff -Naur linux-3.0/drivers/media/rc/nuvoton-cir.c linux-3.0.patch/drivers/media/rc/nuvoton-cir.c
|
||||
--- linux-3.0/drivers/media/rc/nuvoton-cir.c 2011-07-22 04:17:23.000000000 +0200
|
||||
+++ linux-3.0.patch/drivers/media/rc/nuvoton-cir.c 2011-07-22 21:30:48.374591146 +0200
|
||||
@@ -1110,7 +1110,7 @@
|
||||
rdev->dev.parent = &pdev->dev;
|
||||
rdev->driver_name = NVT_DRIVER_NAME;
|
||||
rdev->map_name = RC_MAP_RC6_MCE;
|
||||
- rdev->timeout = MS_TO_NS(100);
|
||||
+ rdev->timeout = US_TO_NS(1000);
|
||||
/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
|
||||
rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
|
||||
#if 0
|
@ -1,11 +0,0 @@
|
||||
--- linux-3.2.2.orig/drivers/media/rc/mceusb.c 2012-01-30 23:37:12.374473509 +0100
|
||||
+++ linux-3.2.2/drivers/media/rc/mceusb.c 2012-01-30 23:40:57.989652931 +0100
|
||||
@@ -350,6 +350,8 @@
|
||||
{ USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
|
||||
/* Formosa21 / eHome Infrared Receiver */
|
||||
{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
|
||||
+ /* Formosa21 / eHome Infrared Receiver */
|
||||
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
|
||||
/* Formosa aim / Trust MCE Infrared Receiver */
|
||||
{ USB_DEVICE(VENDOR_FORMOSA, 0xe017),
|
||||
.driver_info = MCE_GEN2_NO_TX },
|
@ -1,20 +0,0 @@
|
||||
diff -Naur linux-3.6.7/drivers/media/rc/mceusb.c linux-3.6.7.patch/drivers/media/rc/mceusb.c
|
||||
--- linux-3.6.7/drivers/media/rc/mceusb.c 2012-11-29 04:45:51.142129739 +0100
|
||||
+++ linux-3.6.7.patch/drivers/media/rc/mceusb.c 2012-11-29 04:51:30.982828558 +0100
|
||||
@@ -200,6 +200,7 @@
|
||||
#define VENDOR_TIVO 0x105a
|
||||
#define VENDOR_CONEXANT 0x0572
|
||||
#define VENDOR_TWISTEDMELON 0x2596
|
||||
+#define VENDOR_ADAPTEC 0x03f3
|
||||
|
||||
enum mceusb_model_type {
|
||||
MCE_GEN2 = 0, /* Most boards */
|
||||
@@ -400,6 +401,8 @@
|
||||
{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
|
||||
/* Twisted Melon Inc. - Manta Transceiver */
|
||||
{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
|
||||
+ /* Adaptec / HP eHome Receiver */
|
||||
+ { USB_DEVICE(VENDOR_ADAPTEC, 0x0094) },
|
||||
/* Terminating entry */
|
||||
{ }
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
--- linux/drivers/media/rc/ir-rc6-decoder.c 2012-11-25 22:08:13.148418669 -0800
|
||||
+++ linux.patch/drivers/media/rc/ir-rc6-decoder.c 2012-11-25 22:07:48.864417975 -0800
|
||||
@@ -39,7 +39,6 @@
|
||||
#define RC6_STARTBIT_MASK 0x08 /* for the header bits */
|
||||
#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
|
||||
#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */
|
||||
-#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */
|
||||
#ifndef CHAR_BIT
|
||||
#define CHAR_BIT 8 /* Normally in <limits.h> */
|
||||
#endif
|
||||
@@ -242,9 +241,8 @@ again:
|
||||
}
|
||||
|
||||
scancode = data->body;
|
||||
- if (data->count == RC6_6A_32_NBITS &&
|
||||
- (scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
|
||||
- /* MCE RC */
|
||||
+ if (data->count == RC6_6A_32_NBITS) {
|
||||
+ /* MCE compatible RC */
|
||||
toggle = (scancode & RC6_6A_MCE_TOGGLE_MASK) ? 1 : 0;
|
||||
scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
|
||||
} else {
|
@ -1,13 +0,0 @@
|
||||
diff -Naur linux-3.9.4/drivers/media/rc/mceusb.c linux-3.9.4.patch/drivers/media/rc/mceusb.c
|
||||
--- linux-3.9.4/drivers/media/rc/mceusb.c 2013-05-24 20:45:59.000000000 +0200
|
||||
+++ linux-3.9.4.patch/drivers/media/rc/mceusb.c 2013-05-27 12:28:12.811230633 +0200
|
||||
@@ -309,6 +309,9 @@
|
||||
/* SMK/I-O Data GV-MC7/RCKIT Receiver */
|
||||
{ USB_DEVICE(VENDOR_SMK, 0x0353),
|
||||
.driver_info = MCE_GEN2_NO_TX },
|
||||
+ /* SMK Manufacturing, Inc. Receiver */
|
||||
+ { USB_DEVICE(VENDOR_SMK, 0x0357),
|
||||
+ .driver_info = MCE_GEN2_NO_TX },
|
||||
/* Tatung eHome Infrared Transceiver */
|
||||
{ USB_DEVICE(VENDOR_TATUNG, 0x9150) },
|
||||
/* Shuttle eHome Infrared Transceiver */
|
@ -1,61 +0,0 @@
|
||||
Betreff: [RFC] hid/sony: add autorepeat for PS3 remotes
|
||||
Von: David Dillow <dave@thedillows.org>
|
||||
Datum: 28.06.2013 04:28
|
||||
An: linux-input@vger.kernel.org
|
||||
Kopie (CC): Stephan Raue <stephan@openelec.tv>
|
||||
|
||||
Some applications using the PS3 remote would like to have autorepeat
|
||||
from the device. Use the input subsystem's software emulation to provide
|
||||
this capability, and enable those that don't need it to turn it off.
|
||||
---
|
||||
I'm not sure this is the correct approach, or if it is even appropriate
|
||||
for a remote to do autorepeat. However, the media/rc subsystem does do
|
||||
it by default, and it's been requested by users, so there is at least
|
||||
some demand.
|
||||
|
||||
This compiled against the hid-sony driver with the PS3 remote changes
|
||||
merged, but I have done no testing of it. If the approach seems
|
||||
reasonable, I'll try to test it when the MythTV is idle.
|
||||
|
||||
drivers/hid/hid-sony.c | 20 ++++++++++++++++++++
|
||||
1 file changed, 20 insertions(+)
|
||||
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
|
||||
index ecbc749..0bbcd07 100644
|
||||
--- a/drivers/hid/hid-sony.c
|
||||
+++ b/drivers/hid/hid-sony.c
|
||||
@@ -274,6 +274,24 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
return 1;
|
||||
}
|
||||
|
||||
+static int ps3remote_setup_repeat(struct hid_device *hdev)
|
||||
+{
|
||||
+ struct hid_input *hidinput = list_first_entry(&hdev->inputs,
|
||||
+ struct hid_input, list);
|
||||
+ struct input_dev *input = hidinput->input;
|
||||
+
|
||||
+ /*
|
||||
+ * Set up autorepeat defaults per the remote control subsystem;
|
||||
+ * this must be done after hid_hw_start(), as having these non-zero
|
||||
+ * at the time of input_register_device() tells the input system that
|
||||
+ * the hardware does the autorepeat, and the PS3 remote does not.
|
||||
+ */
|
||||
+ set_bit(EV_REP, input->evbit);
|
||||
+ input->rep[REP_DELAY] = 500;
|
||||
+ input->rep[REP_PERIOD] = 125;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
/* Sony Vaio VGX has wrongly mouse pointer declared as constant */
|
||||
static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
||||
@@ -659,6 +677,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
ret = sixaxis_set_operational_bt(hdev);
|
||||
else if (sc->quirks & BUZZ_CONTROLLER)
|
||||
ret = buzz_init(hdev);
|
||||
+ else if (sc->quirks & PS3REMOTE)
|
||||
+ ret = ps3remote_setup_repeat(hdev);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
commit 5a601d61d36236a667cc7d170b300d18dd6240c6
|
||||
Author: Juan J. Sierralta <sierralta@gmail.com>
|
||||
Date: Sun Jul 28 09:26:04 2013 +0300
|
||||
|
||||
Add support for SMK-Link PS3 remote
|
||||
|
||||
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
|
||||
index 396d24d..9eb7129 100644
|
||||
--- a/drivers/hid/hid-core.c
|
||||
+++ b/drivers/hid/hid-core.c
|
||||
@@ -1686,6 +1686,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
|
||||
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
|
||||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
|
||||
index dd0511e..2801df1 100644
|
||||
--- a/drivers/hid/hid-ids.h
|
||||
+++ b/drivers/hid/hid-ids.h
|
||||
@@ -734,6 +734,7 @@
|
||||
#define USB_VENDOR_ID_SKYCABLE 0x1223
|
||||
#define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07
|
||||
|
||||
+#define USB_VENDOR_ID_SMK 0x0609
|
||||
#define USB_VENDOR_ID_SONY 0x054c
|
||||
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
|
||||
#define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE 0x0374
|
||||
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
|
||||
index 8f425e2..614f057 100644
|
||||
--- a/drivers/hid/hid-sony.c
|
||||
+++ b/drivers/hid/hid-sony.c
|
||||
@@ -728,8 +728,12 @@ static const struct hid_device_id sony_devices[] = {
|
||||
/* Logitech Harmony Adapter for PS3 */
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3),
|
||||
.driver_data = PS3REMOTE },
|
||||
+ /* SMK-Link Universal Remote Control VP3700 */
|
||||
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SONY_PS3_BDREMOTE),
|
||||
+ .driver_data = PS3REMOTE },
|
||||
{ }
|
||||
};
|
||||
+
|
||||
MODULE_DEVICE_TABLE(hid, sony_devices);
|
||||
|
||||
static struct hid_driver sony_driver = {
|
@ -1,11 +0,0 @@
|
||||
diff -Naur linux-3.8.4/drivers/input/joystick/xpad.c linux-3.8.4.patch/drivers/input/joystick/xpad.c
|
||||
--- linux-3.8.4/drivers/input/joystick/xpad.c 2013-03-20 21:11:19.000000000 +0100
|
||||
+++ linux-3.8.4.patch/drivers/input/joystick/xpad.c 2013-03-26 20:24:29.273978355 +0100
|
||||
@@ -174,7 +174,6 @@
|
||||
{ 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
|
||||
- { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
|
||||
};
|
||||
|
@ -1,13 +0,0 @@
|
||||
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
|
||||
index c0cd084..c4b279f 100644
|
||||
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
|
||||
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
|
||||
@@ -1388,6 +1388,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = {
|
||||
&rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) },
|
||||
{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
|
||||
&rtl2832u_props, "Crypto ReDi PC 50 A", NULL) },
|
||||
+ { DVB_USB_DEVICE(USB_VID_GTEK, 0xa803,
|
||||
+ &rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
|
@ -1,12 +0,0 @@
|
||||
diff -Naur linux-3.7.2/drivers/media/dvb-frontends/stb0899_drv.c linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_drv.c
|
||||
--- linux-3.7.2/drivers/media/dvb-frontends/stb0899_drv.c 2013-01-11 18:19:28.000000000 +0100
|
||||
+++ linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_drv.c 2013-01-16 10:25:43.479645317 +0100
|
||||
@@ -1581,7 +1581,7 @@
|
||||
.frequency_max = 2150000,
|
||||
.frequency_stepsize = 0,
|
||||
.frequency_tolerance = 0,
|
||||
- .symbol_rate_min = 5000000,
|
||||
+ .symbol_rate_min = 1000000,
|
||||
.symbol_rate_max = 45000000,
|
||||
|
||||
.caps = FE_CAN_INVERSION_AUTO |
|
6101
packages/linux/patches/3.12.8/linux-210-dvbsky.patch
vendored
6101
packages/linux/patches/3.12.8/linux-210-dvbsky.patch
vendored
File diff suppressed because it is too large
Load Diff
@ -1,138 +0,0 @@
|
||||
diff -Naur linux-3.7.2/drivers/media/dvb-frontends/stb0899_algo.c linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_algo.c
|
||||
--- linux-3.7.2/drivers/media/dvb-frontends/stb0899_algo.c 2013-01-11 18:19:28.000000000 +0100
|
||||
+++ linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_algo.c 2013-01-16 10:28:33.633409961 +0100
|
||||
@@ -206,7 +206,6 @@
|
||||
static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state)
|
||||
{
|
||||
struct stb0899_internal *internal = &state->internal;
|
||||
- struct stb0899_params *params = &state->params;
|
||||
|
||||
short int derot_step, derot_freq = 0, derot_limit, next_loop = 3;
|
||||
int index = 0;
|
||||
@@ -216,10 +215,9 @@
|
||||
|
||||
/* timing loop computation & symbol rate optimisation */
|
||||
derot_limit = (internal->sub_range / 2L) / internal->mclk;
|
||||
- derot_step = (params->srate / 2L) / internal->mclk;
|
||||
+ derot_step = internal->derot_step * 4; /* dertot_step = decreasing delta */
|
||||
|
||||
while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) {
|
||||
- index++;
|
||||
derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */
|
||||
|
||||
if (abs(derot_freq) > derot_limit)
|
||||
@@ -230,6 +228,7 @@
|
||||
STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
|
||||
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
|
||||
}
|
||||
+ index++;
|
||||
internal->direction = -internal->direction; /* Change zigzag direction */
|
||||
}
|
||||
|
||||
@@ -278,14 +277,18 @@
|
||||
{
|
||||
struct stb0899_internal *internal = &state->internal;
|
||||
|
||||
- short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3;
|
||||
+ short int derot_freq = 0, last_derot_freq = 0, derot_limit, derot_step, next_loop = 3;
|
||||
int index = 0;
|
||||
+ int base_freq;
|
||||
u8 cfr[2];
|
||||
u8 reg;
|
||||
|
||||
internal->status = NOCARRIER;
|
||||
derot_limit = (internal->sub_range / 2L) / internal->mclk;
|
||||
derot_freq = internal->derot_freq;
|
||||
+ derot_step = internal->derot_step * 2;
|
||||
+ last_derot_freq = internal->derot_freq;
|
||||
+ base_freq = internal->derot_freq;
|
||||
|
||||
reg = stb0899_read_reg(state, STB0899_CFD);
|
||||
STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
|
||||
@@ -294,11 +297,10 @@
|
||||
do {
|
||||
dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk);
|
||||
if (stb0899_check_carrier(state) == NOCARRIER) {
|
||||
- index++;
|
||||
last_derot_freq = derot_freq;
|
||||
- derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */
|
||||
+ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */
|
||||
|
||||
- if(abs(derot_freq) > derot_limit)
|
||||
+ if (derot_freq > base_freq + derot_limit || derot_freq < base_freq - derot_limit)
|
||||
next_loop--;
|
||||
|
||||
if (next_loop) {
|
||||
@@ -310,9 +312,10 @@
|
||||
STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
|
||||
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
|
||||
}
|
||||
+ index++;
|
||||
+ internal->direction = -internal->direction; /* Change zigzag direction */
|
||||
}
|
||||
|
||||
- internal->direction = -internal->direction; /* Change zigzag direction */
|
||||
} while ((internal->status != CARRIEROK) && next_loop);
|
||||
|
||||
if (internal->status == CARRIEROK) {
|
||||
@@ -338,6 +341,7 @@
|
||||
int lock = 0, index = 0, dataTime = 500, loop;
|
||||
u8 reg;
|
||||
|
||||
+ msleep(1);
|
||||
internal->status = NODATA;
|
||||
|
||||
/* RESET FEC */
|
||||
@@ -348,6 +352,7 @@
|
||||
reg = stb0899_read_reg(state, STB0899_TSTRES);
|
||||
STB0899_SETFIELD_VAL(FRESACS, reg, 0);
|
||||
stb0899_write_reg(state, STB0899_TSTRES, reg);
|
||||
+ msleep(1);
|
||||
|
||||
if (params->srate <= 2000000)
|
||||
dataTime = 2000;
|
||||
@@ -363,6 +368,7 @@
|
||||
|
||||
stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */
|
||||
while (1) {
|
||||
+ msleep(1); // Alex: added 1 mSec
|
||||
/* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */
|
||||
reg = stb0899_read_reg(state, STB0899_VSTATUS);
|
||||
lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg);
|
||||
@@ -390,20 +396,21 @@
|
||||
short int derot_freq, derot_step, derot_limit, next_loop = 3;
|
||||
u8 cfr[2];
|
||||
u8 reg;
|
||||
- int index = 1;
|
||||
+ int index = 0;
|
||||
+ int base_freq;
|
||||
|
||||
struct stb0899_internal *internal = &state->internal;
|
||||
- struct stb0899_params *params = &state->params;
|
||||
|
||||
- derot_step = (params->srate / 4L) / internal->mclk;
|
||||
+ derot_step = internal->derot_step;
|
||||
derot_limit = (internal->sub_range / 2L) / internal->mclk;
|
||||
derot_freq = internal->derot_freq;
|
||||
+ base_freq = internal->derot_freq;
|
||||
|
||||
do {
|
||||
if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) {
|
||||
|
||||
derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */
|
||||
- if (abs(derot_freq) > derot_limit)
|
||||
+ if (derot_freq > base_freq + derot_limit || derot_freq < base_freq - derot_limit)
|
||||
next_loop--;
|
||||
|
||||
if (next_loop) {
|
||||
@@ -417,9 +424,9 @@
|
||||
stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
|
||||
|
||||
stb0899_check_carrier(state);
|
||||
- index++;
|
||||
}
|
||||
}
|
||||
+ index++;
|
||||
internal->direction = -internal->direction; /* change zig zag direction */
|
||||
} while ((internal->status != DATAOK) && next_loop);
|
||||
|
26298
packages/linux/patches/3.12.8/linux-221-ngene-octopus.patch
vendored
26298
packages/linux/patches/3.12.8/linux-221-ngene-octopus.patch
vendored
File diff suppressed because it is too large
Load Diff
@ -1,62 +0,0 @@
|
||||
diff -Naur linux-3.7.2/drivers/media/dvb-frontends/stb0899_drv.c linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_drv.c
|
||||
--- linux-3.7.2/drivers/media/dvb-frontends/stb0899_drv.c 2013-01-11 18:19:28.000000000 +0100
|
||||
+++ linux-3.7.2.patch/drivers/media/dvb-frontends/stb0899_drv.c 2013-01-16 10:33:10.323380937 +0100
|
||||
@@ -971,6 +971,16 @@
|
||||
|
||||
*strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val);
|
||||
*strength += 750;
|
||||
+
|
||||
+ const int MIN_STRENGTH_DVBS = 0;
|
||||
+ const int MAX_STRENGTH_DVBS = 680;
|
||||
+ if (*strength < MIN_STRENGTH_DVBS)
|
||||
+ *strength = 0;
|
||||
+ else if(*strength > MAX_STRENGTH_DVBS)
|
||||
+ *strength = 0xFFFF;
|
||||
+ else
|
||||
+ *strength = (*strength - MIN_STRENGTH_DVBS) * 0xFFFF / (MAX_STRENGTH_DVBS - MIN_STRENGTH_DVBS);
|
||||
+
|
||||
dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm",
|
||||
val & 0xff, *strength);
|
||||
}
|
||||
@@ -983,6 +993,7 @@
|
||||
|
||||
*strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
|
||||
*strength += 950;
|
||||
+ *strength = *strength << 4;
|
||||
dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
|
||||
val & 0x3fff, *strength);
|
||||
}
|
||||
@@ -1016,6 +1027,16 @@
|
||||
val = MAKEWORD16(buf[0], buf[1]);
|
||||
|
||||
*snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val);
|
||||
+
|
||||
+ const int MIN_SNR_DVBS = 0;
|
||||
+ const int MAX_SNR_DVBS = 200;
|
||||
+ if (*snr < MIN_SNR_DVBS)
|
||||
+ *snr = 0;
|
||||
+ else if(*snr > MAX_SNR_DVBS)
|
||||
+ *snr = 0xFFFF;
|
||||
+ else
|
||||
+ *snr = (*snr - MIN_SNR_DVBS) * 0xFFFF / (MAX_SNR_DVBS - MIN_SNR_DVBS);
|
||||
+
|
||||
dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n",
|
||||
buf[0], buf[1], val, *snr);
|
||||
}
|
||||
@@ -1040,6 +1061,16 @@
|
||||
val = (quantn - estn) / 10;
|
||||
}
|
||||
*snr = val;
|
||||
+
|
||||
+ const int MIN_SNR_DVBS2 = 10;
|
||||
+ const int MAX_SNR_DVBS2 = 70;
|
||||
+ if (*snr < MIN_SNR_DVBS2)
|
||||
+ *snr = 0;
|
||||
+ else if(*snr > MAX_SNR_DVBS2)
|
||||
+ *snr = 0xFFFF;
|
||||
+ else
|
||||
+ *snr = (*snr - MIN_SNR_DVBS2) * 0xFFFF / (MAX_SNR_DVBS2 - MIN_SNR_DVBS2);
|
||||
+
|
||||
dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm",
|
||||
quant, quantn, est, estn, val);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
diff -Naur linux-3.7.9/drivers/media/usb/dvb-usb/pctv452e.c linux-3.7.9.patch/drivers/media/usb/dvb-usb/pctv452e.c
|
||||
--- linux-3.7.9/drivers/media/usb/dvb-usb/pctv452e.c 2013-01-11 18:19:28.000000000 +0100
|
||||
+++ linux-3.7.9.patch/drivers/media/usb/dvb-usb/pctv452e.c 2013-01-16 10:35:01.131342123 +0100
|
||||
@@ -995,11 +995,11 @@
|
||||
/* parameter for the MPEG2-data transfer */
|
||||
.stream = {
|
||||
.type = USB_ISOC,
|
||||
- .count = 7,
|
||||
+ .count = 4,
|
||||
.endpoint = 0x02,
|
||||
.u = {
|
||||
.isoc = {
|
||||
- .framesperurb = 4,
|
||||
+ .framesperurb = 64,
|
||||
.framesize = 940,
|
||||
.interval = 1
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c 2013-07-21 16:06:37.443909481 +0200
|
||||
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c 2013-07-21 16:11:10.696335476 +0200
|
||||
@@ -1470,15 +1470,18 @@
|
||||
}
|
||||
|
||||
#define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c
|
||||
+#define BRCMF_USB_VENDOR_ID_LINKSYS 0x13b1
|
||||
#define BRCMF_USB_DEVICE_ID_43143 0xbd1e
|
||||
#define BRCMF_USB_DEVICE_ID_43236 0xbd17
|
||||
#define BRCMF_USB_DEVICE_ID_43242 0xbd1f
|
||||
+#define BRCMF_USB_DEVICE_ID_AE2500 0x003a
|
||||
#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc
|
||||
|
||||
static struct usb_device_id brcmf_usb_devid_table[] = {
|
||||
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
|
||||
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
|
||||
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
|
||||
+ { USB_DEVICE(BRCMF_USB_VENDOR_ID_LINKSYS, BRCMF_USB_DEVICE_ID_AE2500) },
|
||||
/* special entry for device with firmware loaded and running */
|
||||
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
|
||||
{ }
|
@ -1,11 +0,0 @@
|
||||
--- linux-3.9.2/drivers/net/ethernet/broadcom/tg3.c 2013-05-11 18:19:28.000000000 +0400
|
||||
+++ linux-3.9.2/drivers/net/ethernet/broadcom/tg3.c 2013-05-25 20:55:00.282972605 +0400
|
||||
@@ -333,6 +333,8 @@
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5720)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57762)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57766)},
|
||||
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57782)},
|
||||
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57786)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5762)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5725)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5727)},
|
@ -1,28 +0,0 @@
|
||||
From 62330f8f9b6105bfe201f52b7ed86ea6ce3d5901 Mon Sep 17 00:00:00 2001
|
||||
From: popcornmix <popcornmix@gmail.com>
|
||||
Date: Sun, 8 Sep 2013 10:33:51 +0100
|
||||
Subject: [PATCH] Add Ubiquiti WifiStation USB id to ath9k wifi driver
|
||||
|
||||
---
|
||||
drivers/net/wireless/ath/ath9k/hif_usb.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
|
||||
index 75a6376..8cb8d8d 100644
|
||||
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
|
||||
@@ -37,9 +37,11 @@
|
||||
{ USB_DEVICE(0x13D3, 0x3350) }, /* Azurewave */
|
||||
{ USB_DEVICE(0x04CA, 0x4605) }, /* Liteon */
|
||||
{ USB_DEVICE(0x040D, 0x3801) }, /* VIA */
|
||||
+ { USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */
|
||||
{ USB_DEVICE(0x0cf3, 0xb003) }, /* Ubiquiti WifiStation Ext */
|
||||
{ USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */
|
||||
{ USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
|
||||
+ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */
|
||||
|
||||
{ USB_DEVICE(0x0cf3, 0x7015),
|
||||
.driver_info = AR9287_USB }, /* Atheros */
|
||||
--
|
||||
1.8.4
|
||||
|
@ -1,10 +0,0 @@
|
||||
--- a/drivers/net/wireless/rt2x00/rt2800usb.c 2013-10-08 15:14:44.844047190 +0200
|
||||
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c 2013-10-08 15:15:00.279904575 +0200
|
||||
@@ -976,6 +976,7 @@
|
||||
{ USB_DEVICE(0x0411, 0x015d) },
|
||||
{ USB_DEVICE(0x0411, 0x016f) },
|
||||
{ USB_DEVICE(0x0411, 0x01a2) },
|
||||
+ { USB_DEVICE(0x0411, 0x01a8) },
|
||||
{ USB_DEVICE(0x0411, 0x01ee) },
|
||||
/* Corega */
|
||||
{ USB_DEVICE(0x07aa, 0x002f) },
|
@ -1,11 +0,0 @@
|
||||
diff -Naur linux-3.10.16/drivers/staging/rtl8712/usb_intf.c linux-3.10.16.patch/drivers/staging/rtl8712/usb_intf.c
|
||||
--- linux-3.10.16/drivers/staging/rtl8712/usb_intf.c 2013-10-14 01:08:56.000000000 +0200
|
||||
+++ linux-3.10.16.patch/drivers/staging/rtl8712/usb_intf.c 2013-10-16 13:27:44.032951265 +0200
|
||||
@@ -92,6 +92,7 @@
|
||||
{USB_DEVICE(0x0DF6, 0x005B)},
|
||||
{USB_DEVICE(0x0DF6, 0x005D)},
|
||||
{USB_DEVICE(0x0DF6, 0x0063)},
|
||||
+ {USB_DEVICE(0x0DF6, 0x006C)},
|
||||
/* Sweex */
|
||||
{USB_DEVICE(0x177F, 0x0154)},
|
||||
/* Thinkware */
|
@ -1,12 +0,0 @@
|
||||
diff -uNr linux-3.6.4-orig/arch/x86/kernel/tsc.c linux-3.6.4-new/arch/x86/kernel/tsc.c
|
||||
--- linux-3.6.4-orig/arch/x86/kernel/tsc.c 2012-11-03 14:19:55.000000000 +0100
|
||||
+++ linux-3.6.4-new/arch/x86/kernel/tsc.c 2012-11-03 14:23:05.000000000 +0100
|
||||
@@ -374,7 +374,7 @@
|
||||
goto success;
|
||||
}
|
||||
}
|
||||
- pr_err("Fast TSC calibration failed\n");
|
||||
+ pr_info("Fast TSC calibration failed\n");
|
||||
return 0;
|
||||
|
||||
success:
|
File diff suppressed because it is too large
Load Diff
@ -1,78 +0,0 @@
|
||||
commit aad0b407edfef6e2527a9bb877ce754e1efb7b10
|
||||
Author: Stefan Saraev <stefan@saraev.ca>
|
||||
Date: Mon Jul 1 13:06:10 2013 +0300
|
||||
|
||||
ALSA: hda - Avoid outputting HDMI audio before prepare() and after close()
|
||||
|
||||
adapted to 3.10
|
||||
|
||||
From a6024295fd3290a8c9c5519a03316081ee82378a Mon Sep 17 00:00:00 2001
|
||||
From: Anssi Hannula <anssi.hannula@iki.fi>
|
||||
Date: Sat, 16 Feb 2013 17:42:46 +0200
|
||||
Subject: [PATCH] ALSA: hda - Avoid outputting HDMI audio before prepare() and
|
||||
after close()
|
||||
|
||||
Some HDMI codecs (at least NVIDIA 0x10de000b:0x10de0101:0x100100) start
|
||||
transmitting an empty audio stream as soon as PIN_OUT and AC_DIG1_ENABLE
|
||||
are enabled.
|
||||
|
||||
Since commit 6169b673618bf0b2518ce413b54925782a603f06 ("ALSA: hda -
|
||||
Always turn on pins for HDMI/DP") this happens at first open() time, and
|
||||
will continue even after close().
|
||||
|
||||
Additionally, some codecs (at least Intel PantherPoint HDMI) currently
|
||||
continue transmitting HDMI audio even after close() in case some actual
|
||||
audio was output after open() (this happens regardless of PIN_OUT).
|
||||
|
||||
Empty HDMI audio transmission when not intended has the effect that a
|
||||
possible HDMI audio sink/receiver may prefer the empty HDMI audio stream
|
||||
over an actual audio stream on its S/PDIF inputs.
|
||||
|
||||
To avoid the issue before first prepare(), set stream format to 0 on
|
||||
codec initialization. 0 is not a valid format value for HDMI and will
|
||||
prevent the audio stream from being output.
|
||||
|
||||
Additionally, at close() time, make sure that the stream is cleaned up.
|
||||
This will ensure that the format is reset to 0 at that time, preventing
|
||||
audio from being output in that case.
|
||||
|
||||
Thanks to OpenELEC developers and users for their help in investigating
|
||||
this issue on the affected NVIDIA "ION2" hardware. Testing of the final
|
||||
version on NVIDIA ION2 was done by OpenELEC user "MrXIII". Testing on
|
||||
Intel PantherPoint was done by myself.
|
||||
|
||||
Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
|
||||
Cc: stable@vger.kernel.org
|
||||
|
||||
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
|
||||
index e12f7a0..9bfdd51 100644
|
||||
--- a/sound/pci/hda/patch_hdmi.c
|
||||
+++ b/sound/pci/hda/patch_hdmi.c
|
||||
@@ -1360,6 +1360,14 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
+ /*
|
||||
+ * Some HDMI codecs (at least NVIDIA 0x10de000b:0x10de0101:0x100100)
|
||||
+ * start transmitting an empty audio stream as soon as PIN_OUT and
|
||||
+ * AC_DIG1_ENABLE are enabled, which happens at open() time.
|
||||
+ * To avoid that, set format to 0, which is not valid for HDMI.
|
||||
+ */
|
||||
+ snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
|
||||
+
|
||||
if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
|
||||
spec->cvt_nids[spec->num_cvts] = cvt_nid;
|
||||
spec->num_cvts++;
|
||||
@@ -1474,6 +1482,12 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
|
||||
if (hinfo->nid) {
|
||||
+ /*
|
||||
+ * Make sure no empty audio is output after this point by
|
||||
+ * setting stream format to 0, which is not valid for HDMI.
|
||||
+ */
|
||||
+ __snd_hda_codec_cleanup_stream(codec, hinfo->nid, 1);
|
||||
+
|
||||
cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
|
||||
if (snd_BUG_ON(cvt_idx < 0))
|
||||
return -EINVAL;
|
@ -1,805 +0,0 @@
|
||||
Combined patch for ATI/AMD HDMI.
|
||||
|
||||
Actual patchset has been posted to ALSA-devel.
|
||||
|
||||
--
|
||||
Anssi Hannula <anssi.hannula@iki.fi>
|
||||
|
||||
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
|
||||
index d0d7ac1e..750841e 100644
|
||||
--- a/sound/pci/hda/hda_eld.c
|
||||
+++ b/sound/pci/hda/hda_eld.c
|
||||
@@ -2,6 +2,7 @@
|
||||
* Generic routines and proc interface for ELD(EDID Like Data) information
|
||||
*
|
||||
* Copyright(c) 2008 Intel Corporation.
|
||||
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
|
||||
*
|
||||
* Authors:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
@@ -316,6 +317,9 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
|
||||
AC_DIPSIZE_ELD_BUF);
|
||||
}
|
||||
|
||||
+static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
|
||||
+ unsigned char *buf, int *eld_size);
|
||||
+
|
||||
int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned char *buf, int *eld_size)
|
||||
{
|
||||
@@ -323,6 +327,9 @@ int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
|
||||
int ret = 0;
|
||||
int size;
|
||||
|
||||
+ if (is_atihdmi(codec))
|
||||
+ return atihdmi_get_eld(codec, nid, buf, eld_size);
|
||||
+
|
||||
/*
|
||||
* ELD size is initialized to zero in caller function. If no errors and
|
||||
* ELD is valid, actual eld_size is assigned.
|
||||
@@ -671,3 +678,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e,
|
||||
hinfo->maxbps = min(hinfo->maxbps, maxbps);
|
||||
hinfo->channels_max = min(hinfo->channels_max, channels_max);
|
||||
}
|
||||
+
|
||||
+
|
||||
+/* ATI/AMD specific stuff (ELD emulation) */
|
||||
+
|
||||
+#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
|
||||
+#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
|
||||
+#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
|
||||
+#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
|
||||
+#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
|
||||
+#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
|
||||
+#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
|
||||
+
|
||||
+#define ATI_SPKALLOC_SPKALLOC 0x007f
|
||||
+#define ATI_SPKALLOC_TYPE_HDMI 0x0100
|
||||
+#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
|
||||
+
|
||||
+/* first three bytes are just standard SAD */
|
||||
+#define ATI_AUDIODESC_CHANNELS 0x00000007
|
||||
+#define ATI_AUDIODESC_RATES 0x0000ff00
|
||||
+#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
|
||||
+
|
||||
+/* in standard HDMI VSDB format */
|
||||
+#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
|
||||
+#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
|
||||
+
|
||||
+enum ati_sink_info_idx {
|
||||
+ ATI_INFO_IDX_MANUFACTURER_ID = 0,
|
||||
+ ATI_INFO_IDX_PRODUCT_ID = 1,
|
||||
+ ATI_INFO_IDX_SINK_DESC_LEN = 2,
|
||||
+ ATI_INFO_IDX_PORT_ID_LOW = 3,
|
||||
+ ATI_INFO_IDX_PORT_ID_HIGH = 4,
|
||||
+ ATI_INFO_IDX_SINK_DESC_FIRST = 5,
|
||||
+ ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
|
||||
+};
|
||||
+
|
||||
+static int atihdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
|
||||
+ unsigned char *buf, int *eld_size)
|
||||
+{
|
||||
+ int spkalloc, ati_sad, aud_synch;
|
||||
+ int sink_desc_len = 0;
|
||||
+ int pos, i;
|
||||
+
|
||||
+ /* ATI/AMD does not have ELD, emulate it */
|
||||
+
|
||||
+ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
|
||||
+
|
||||
+ if (!spkalloc) {
|
||||
+ snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
|
||||
+
|
||||
+ /* version */
|
||||
+ buf[0] = ELD_VER_CEA_861D << 3;
|
||||
+
|
||||
+ /* speaker allocation from EDID */
|
||||
+ buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
|
||||
+
|
||||
+ /* is DisplayPort? */
|
||||
+ if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
|
||||
+ buf[5] |= 0x04;
|
||||
+
|
||||
+ pos = ELD_FIXED_BYTES;
|
||||
+
|
||||
+ if (is_amdhdmi_rev3(codec)) {
|
||||
+ int sink_info;
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
|
||||
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+ put_unaligned_le32(sink_info, buf + 8);
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
|
||||
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+ put_unaligned_le32(sink_info, buf + 12);
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
|
||||
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+ put_unaligned_le16(sink_info, buf + 16);
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
|
||||
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+ put_unaligned_le16(sink_info, buf + 18);
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
|
||||
+ sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+
|
||||
+ if (sink_desc_len > ELD_MAX_MNL) {
|
||||
+ snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
|
||||
+ sink_desc_len);
|
||||
+ sink_desc_len = ELD_MAX_MNL;
|
||||
+ }
|
||||
+
|
||||
+ buf[4] |= sink_desc_len;
|
||||
+
|
||||
+ for (i = 0; i < sink_desc_len; i++) {
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
|
||||
+ buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
|
||||
+ if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
|
||||
+ continue; /* not handled by ATI/AMD */
|
||||
+
|
||||
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
|
||||
+ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
|
||||
+
|
||||
+ if (ati_sad & ATI_AUDIODESC_RATES) {
|
||||
+ /* format is supported, copy SAD as-is */
|
||||
+ buf[pos++] = (ati_sad & 0x0000ff) >> 0;
|
||||
+ buf[pos++] = (ati_sad & 0x00ff00) >> 8;
|
||||
+ buf[pos++] = (ati_sad & 0xff0000) >> 16;
|
||||
+ }
|
||||
+
|
||||
+ if (i == AUDIO_CODING_TYPE_LPCM
|
||||
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
|
||||
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
|
||||
+ /* for PCM there is a separate stereo rate mask */
|
||||
+ buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
|
||||
+ /* rates from the extra byte */
|
||||
+ buf[pos++] = (ati_sad & 0xff000000) >> 24;
|
||||
+ buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (pos == ELD_FIXED_BYTES + sink_desc_len) {
|
||||
+ snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
|
||||
+ if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
|
||||
+ int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1;
|
||||
+ int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1;
|
||||
+
|
||||
+ if (video_latency > audio_latency)
|
||||
+ buf[6] = min(video_latency - audio_latency, 0xfa);
|
||||
+ }
|
||||
+
|
||||
+ /* Baseline length */
|
||||
+ buf[2] = pos - 4;
|
||||
+
|
||||
+ /* SAD count */
|
||||
+ buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
|
||||
+
|
||||
+ *eld_size = pos;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
|
||||
index 2e7493e..7c0b89e 100644
|
||||
--- a/sound/pci/hda/hda_local.h
|
||||
+++ b/sound/pci/hda/hda_local.h
|
||||
@@ -786,4 +786,9 @@ static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
|
||||
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
|
||||
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
|
||||
|
||||
+/* shared with patch_hdmi.c and hda_eld.c */
|
||||
+#define is_atihdmi(codec) (((codec)->vendor_id & 0xffff0000) == 0x10020000)
|
||||
+#define is_amdhdmi_rev3(codec) \
|
||||
+ ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300)
|
||||
+
|
||||
#endif /* __SOUND_HDA_LOCAL_H */
|
||||
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
|
||||
index 3d8cd044..22f30fe 100644
|
||||
--- a/sound/pci/hda/patch_hdmi.c
|
||||
+++ b/sound/pci/hda/patch_hdmi.c
|
||||
@@ -6,6 +6,7 @@
|
||||
* Copyright (c) 2006 ATI Technologies Inc.
|
||||
* Copyright (c) 2008 NVIDIA Corp. All rights reserved.
|
||||
* Copyright (c) 2008 Wei Ni <wni@nvidia.com>
|
||||
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
|
||||
*
|
||||
* Authors:
|
||||
* Wu Fengguang <wfg@linux.intel.com>
|
||||
@@ -46,6 +47,9 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
|
||||
|
||||
#define is_haswell(codec) ((codec)->vendor_id == 0x80862807)
|
||||
|
||||
+/* is_atihdmi() and is_amdhdmi_rev3() are in hda_local.h */
|
||||
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3(codec)
|
||||
+
|
||||
struct hdmi_spec_per_cvt {
|
||||
hda_nid_t cvt_nid;
|
||||
int assigned;
|
||||
@@ -89,7 +93,7 @@ struct hdmi_spec {
|
||||
|
||||
struct hdmi_eld temp_eld;
|
||||
/*
|
||||
- * Non-generic ATI/NVIDIA specific
|
||||
+ * Non-generic VIA/NVIDIA specific
|
||||
*/
|
||||
struct hda_multi_out multiout;
|
||||
struct hda_pcm_stream pcm_playback;
|
||||
@@ -573,6 +577,20 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels)
|
||||
return ca;
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
+static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot);
|
||||
+
|
||||
+static int hdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
|
||||
+{
|
||||
+ if (is_atihdmi(codec))
|
||||
+ return atihdmi_get_chan_slot(codec, pin_nid, asp_slot);
|
||||
+
|
||||
+ return snd_hda_codec_read(codec, pin_nid, 0,
|
||||
+ AC_VERB_GET_HDMI_CHAN_SLOT,
|
||||
+ asp_slot);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static void hdmi_debug_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid)
|
||||
{
|
||||
@@ -581,14 +599,26 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
|
||||
int slot;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
- slot = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
- AC_VERB_GET_HDMI_CHAN_SLOT, i);
|
||||
+ slot = hdmi_get_chan_slot(codec, pin_nid, i);
|
||||
printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
|
||||
slot >> 4, slot & 0xf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
+ int chanslot_setup);
|
||||
+
|
||||
+static int hdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
+ int chanslot_setup)
|
||||
+{
|
||||
+ if (is_atihdmi(codec))
|
||||
+ return atihdmi_set_chan_slot(codec, pin_nid, chanslot_setup);
|
||||
+
|
||||
+ return snd_hda_codec_write(codec, pin_nid, 0,
|
||||
+ AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
+ chanslot_setup);
|
||||
+}
|
||||
|
||||
static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
|
||||
hda_nid_t pin_nid,
|
||||
@@ -617,9 +647,8 @@ static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
- err = snd_hda_codec_write(codec, pin_nid, 0,
|
||||
- AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
- non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
|
||||
+ err = hdmi_set_chan_slot(codec, pin_nid,
|
||||
+ non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]);
|
||||
if (err) {
|
||||
snd_printdd(KERN_NOTICE
|
||||
"HDMI: channel mapping failed\n");
|
||||
@@ -728,8 +757,7 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
|
||||
else
|
||||
val = 0xf;
|
||||
val |= (i << 4);
|
||||
- err = snd_hda_codec_write(codec, pin_nid, 0,
|
||||
- AC_VERB_SET_HDMI_CHAN_SLOT, val);
|
||||
+ err = hdmi_set_chan_slot(codec, pin_nid, val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -883,6 +911,8 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
return true;
|
||||
}
|
||||
|
||||
+static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca);
|
||||
+
|
||||
static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
||||
struct hdmi_spec_per_pin *per_pin,
|
||||
bool non_pcm)
|
||||
@@ -912,6 +942,16 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
|
||||
if (ca < 0)
|
||||
ca = 0;
|
||||
|
||||
+ if (is_atihdmi(codec)) {
|
||||
+ /* for ATI/AMD we just want to map channels and set ca */
|
||||
+ hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
|
||||
+ channels, per_pin->chmap,
|
||||
+ per_pin->chmap_set);
|
||||
+ atihdmi_set_ca(codec, pin_nid, ca);
|
||||
+ per_pin->non_pcm = non_pcm;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
memset(&ai, 0, sizeof(ai));
|
||||
if (eld->info.conn_type == 0) { /* HDMI */
|
||||
struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
|
||||
@@ -1100,7 +1140,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
|
||||
new_pinctl);
|
||||
|
||||
}
|
||||
- if (is_hbr_format(format) && !new_pinctl) {
|
||||
+ if (is_hbr_format(format) && !new_pinctl && !is_atihdmi(codec)) {
|
||||
snd_printdd("hdmi_setup_stream: HBR is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1603,6 +1643,8 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int atihdmi_swap_fc_lfe(int pos);
|
||||
+
|
||||
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
unsigned int size, unsigned int __user *tlv)
|
||||
{
|
||||
@@ -1613,6 +1655,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
FL | FR | RL | RR | LFE | FC | RLC | RRC;
|
||||
unsigned int __user *dst;
|
||||
int chs, count = 0;
|
||||
+ int tlv_type = SNDRV_CTL_TLVT_CHMAP_VAR;
|
||||
+
|
||||
+ if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
|
||||
+ tlv_type = SNDRV_CTL_TLVT_CHMAP_PAIRED;
|
||||
|
||||
if (size < 8)
|
||||
return -ENOMEM;
|
||||
@@ -1620,19 +1666,35 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
return -EFAULT;
|
||||
size -= 8;
|
||||
dst = tlv + 2;
|
||||
- for (chs = 2; chs <= spec->channels_max; chs++) {
|
||||
+ for (chs = 2; chs <= spec->channels_max;
|
||||
+ chs += (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) ? 2 : 1) {
|
||||
int i, c;
|
||||
struct cea_channel_speaker_allocation *cap;
|
||||
cap = channel_allocations;
|
||||
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
|
||||
int chs_bytes = chs * 4;
|
||||
- if (cap->channels != chs)
|
||||
+
|
||||
+ if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED) {
|
||||
+ int chanpairs = 0;
|
||||
+ /* in paired mode we need to take into account
|
||||
+ * the occupied channel pairs instead of just the
|
||||
+ * channel count */
|
||||
+ for (c = 0; c < 7; c += 2) {
|
||||
+ if (cap->speakers[c] || cap->speakers[c+1])
|
||||
+ chanpairs++;
|
||||
+ }
|
||||
+ if (chanpairs * 2 != chs)
|
||||
+ continue;
|
||||
+
|
||||
+ } else if (cap->channels != chs)
|
||||
continue;
|
||||
+
|
||||
if (cap->spk_mask & ~valid_mask)
|
||||
continue;
|
||||
if (size < 8)
|
||||
return -ENOMEM;
|
||||
- if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) ||
|
||||
+
|
||||
+ if (put_user(tlv_type, dst) ||
|
||||
put_user(chs_bytes, dst + 1))
|
||||
return -EFAULT;
|
||||
dst += 2;
|
||||
@@ -1643,10 +1705,27 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
size -= chs_bytes;
|
||||
count += chs_bytes;
|
||||
for (c = 7; c >= 0; c--) {
|
||||
- int spk = cap->speakers[c];
|
||||
- if (!spk)
|
||||
- continue;
|
||||
- if (put_user(spk_to_chmap(spk), dst))
|
||||
+ int spk;
|
||||
+ int chan = c;
|
||||
+ int chpos;
|
||||
+
|
||||
+ if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED)
|
||||
+ chan = 7 - atihdmi_swap_fc_lfe(7 - chan);
|
||||
+
|
||||
+ spk = cap->speakers[chan];
|
||||
+ if (spk)
|
||||
+ chpos = spk_to_chmap(spk);
|
||||
+ else {
|
||||
+ /* We need to reserve an N/A channel in paired mode
|
||||
+ * if the companion channel is occupied. */
|
||||
+ if (tlv_type == SNDRV_CTL_TLVT_CHMAP_PAIRED
|
||||
+ && cap->speakers[chan + (chan % 2 ? -1 : 1)])
|
||||
+ chpos = SNDRV_CHMAP_NA;
|
||||
+ else
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (put_user(chpos, dst))
|
||||
return -EFAULT;
|
||||
dst++;
|
||||
}
|
||||
@@ -1672,6 +1751,18 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
|
||||
+ int chs, unsigned char *map);
|
||||
+
|
||||
+static int hdmi_chmap_check_order(struct hda_codec *codec, int ca,
|
||||
+ int chs, unsigned char *map)
|
||||
+{
|
||||
+ if (is_atihdmi(codec) && !has_amd_full_remap_support(codec))
|
||||
+ return atihdmi_pairwise_chmap_check_order(codec, ca, chs, map);
|
||||
+
|
||||
+ return 0; /* anything can be remapped as needed */
|
||||
+}
|
||||
+
|
||||
static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
@@ -1683,7 +1774,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
unsigned int ctl_idx;
|
||||
struct snd_pcm_substream *substream;
|
||||
unsigned char chmap[8];
|
||||
- int i, ca, prepared = 0;
|
||||
+ int i, err, ca, prepared = 0;
|
||||
|
||||
ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
||||
substream = snd_pcm_chmap_substream(info, ctl_idx);
|
||||
@@ -1707,6 +1798,9 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
|
||||
ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
|
||||
if (ca < 0)
|
||||
return -EINVAL;
|
||||
+ err = hdmi_chmap_check_order(codec, ca, ARRAY_SIZE(chmap), chmap);
|
||||
+ if (err < 0)
|
||||
+ return -EINVAL;
|
||||
per_pin->chmap_set = true;
|
||||
memcpy(per_pin->chmap, chmap, sizeof(chmap));
|
||||
if (prepared)
|
||||
@@ -2551,13 +2645,182 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
||||
|
||||
/*
|
||||
* ATI-specific implementations
|
||||
- *
|
||||
- * FIXME: we may omit the whole this and use the generic code once after
|
||||
- * it's confirmed to work.
|
||||
*/
|
||||
|
||||
-#define ATIHDMI_CVT_NID 0x02 /* audio converter */
|
||||
-#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
|
||||
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
|
||||
+#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
|
||||
+#define ATI_VERB_SET_DOWNMIX_INFO 0x772
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_01 0x777
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_23 0x778
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_45 0x779
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
|
||||
+#define ATI_VERB_SET_HBR_CONTROL 0x77c
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_1 0x785
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_3 0x786
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_5 0x787
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_7 0x788
|
||||
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
|
||||
+#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
|
||||
+#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
|
||||
+#define ATI_VERB_GET_HBR_CONTROL 0xf7c
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
|
||||
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
|
||||
+
|
||||
+/* AMD specific HDA cvt verbs */
|
||||
+#define ATI_VERB_SET_RAMP_RATE 0x770
|
||||
+#define ATI_VERB_GET_RAMP_RATE 0xf70
|
||||
+
|
||||
+#define ATI_OUT_ENABLE 0x1
|
||||
+
|
||||
+#define ATI_HBR_CAPABLE 0x01
|
||||
+#define ATI_HBR_ENABLE 0x10
|
||||
+
|
||||
+static void atihdmi_set_ca(struct hda_codec *codec, hda_nid_t pin_nid, int ca)
|
||||
+{
|
||||
+ printk("ATI: setting ca %d\n", ca);
|
||||
+ snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
|
||||
+}
|
||||
+
|
||||
+static int atihdmi_swap_fc_lfe(int pos)
|
||||
+{
|
||||
+ /*
|
||||
+ * Older ATI/AMD without channel-wise mapping
|
||||
+ * have automatic FC/LFE swap built-in.
|
||||
+ */
|
||||
+
|
||||
+ switch (pos) {
|
||||
+ /* see channel_allocations[].speakers[] */
|
||||
+ case 2: return 3;
|
||||
+ case 3: return 2;
|
||||
+ default: break;
|
||||
+ }
|
||||
+
|
||||
+ return pos;
|
||||
+}
|
||||
+
|
||||
+static int atihdmi_pairwise_chmap_check_order(struct hda_codec *codec, int ca,
|
||||
+ int chs, unsigned char *map)
|
||||
+{
|
||||
+ struct cea_channel_speaker_allocation *cap;
|
||||
+ int i, j;
|
||||
+
|
||||
+ /* check that only channel pairs need to be remapped on old ATI/AMD */
|
||||
+
|
||||
+ cap = &channel_allocations[get_channel_allocation_order(ca)];
|
||||
+ for (i = 0; i < chs; ++i) {
|
||||
+ int mask = to_spk_mask(map[i]);
|
||||
+ bool ok = false;
|
||||
+ bool companion_ok = false;
|
||||
+
|
||||
+ if (!mask)
|
||||
+ continue;
|
||||
+
|
||||
+ for (j = 0 + i % 2; j < 8; j += 2) {
|
||||
+ int chan_idx = 7 - atihdmi_swap_fc_lfe(j);
|
||||
+ if (cap->speakers[chan_idx] == mask) {
|
||||
+ /* channel is in a supported position */
|
||||
+ ok = true;
|
||||
+
|
||||
+ if (i % 2 == 0 && i + 1 < chs) {
|
||||
+ /* even channel, check the odd companion */
|
||||
+ int comp_chan_idx = 7 - atihdmi_swap_fc_lfe(j + 1);
|
||||
+ int comp_mask_req = to_spk_mask(map[i+1]);
|
||||
+ int comp_mask_act = cap->speakers[comp_chan_idx];
|
||||
+
|
||||
+ if (comp_mask_req == comp_mask_act)
|
||||
+ companion_ok = true;
|
||||
+ else
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!ok)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (companion_ok)
|
||||
+ i++; /* companion channel already checked */
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int atihdmi_set_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid,
|
||||
+ int chanslot_setup)
|
||||
+{
|
||||
+ int hdmi_slot = chanslot_setup & 0xf;
|
||||
+ int stream_channel = chanslot_setup >> 4;
|
||||
+ int verb;
|
||||
+ int ati_channel_setup = 0;
|
||||
+
|
||||
+ if (hdmi_slot > 7)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!has_amd_full_remap_support(codec)) {
|
||||
+ hdmi_slot = atihdmi_swap_fc_lfe(hdmi_slot);
|
||||
+
|
||||
+ /* In case this is an odd slot but without stream channel, do not
|
||||
+ * disable the slot since the corresponding even slot could have a
|
||||
+ * channel. In case neither have a channel, the slot pair will be
|
||||
+ * disabled when this function is called for the even slot. */
|
||||
+ if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
|
||||
+ return 0;
|
||||
+
|
||||
+ hdmi_slot -= hdmi_slot % 2;
|
||||
+
|
||||
+ if (stream_channel != 0xf)
|
||||
+ stream_channel -= stream_channel % 2;
|
||||
+ }
|
||||
+
|
||||
+ verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
|
||||
+
|
||||
+ /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
|
||||
+
|
||||
+ if (stream_channel != 0xf)
|
||||
+ ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
|
||||
+
|
||||
+ return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
+static int atihdmi_get_chan_slot(struct hda_codec *codec, hda_nid_t pin_nid, int asp_slot)
|
||||
+{
|
||||
+ bool was_odd = false;
|
||||
+ int ati_asp_slot = asp_slot;
|
||||
+ int verb;
|
||||
+ int ati_channel_setup;
|
||||
+
|
||||
+ /* emulate AC_VERB_GET_HDMI_CHAN_SLOT */
|
||||
+
|
||||
+ if (asp_slot > 7)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!has_amd_full_remap_support(codec)) {
|
||||
+ ati_asp_slot = atihdmi_swap_fc_lfe(asp_slot);
|
||||
+ if (ati_asp_slot % 2 != 0) {
|
||||
+ ati_asp_slot -= 1;
|
||||
+ was_odd = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
|
||||
+
|
||||
+ ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
|
||||
+
|
||||
+ if (!(ati_channel_setup & ATI_OUT_ENABLE))
|
||||
+ return (0xf << 4) | asp_slot;
|
||||
+
|
||||
+ return ((ati_channel_setup & 0xf0) + ((!!was_odd) << 4)) | asp_slot;
|
||||
+}
|
||||
+#endif
|
||||
|
||||
static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
struct hda_codec *codec,
|
||||
@@ -2565,34 +2828,117 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
unsigned int format,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
+ hda_nid_t cvt_nid = hinfo->nid;
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
- struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
|
||||
- int chans = substream->runtime->channels;
|
||||
- int i, err;
|
||||
+ int pin_idx = hinfo_to_pin_index(spec, hinfo);
|
||||
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
+ hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
+ int hbr_ctl, hbr_ctl_new;
|
||||
|
||||
- err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
|
||||
- substream);
|
||||
- if (err < 0)
|
||||
+ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
|
||||
+ if (hbr_ctl & ATI_HBR_CAPABLE) {
|
||||
+ if (is_hbr_format(format))
|
||||
+ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
|
||||
+ else
|
||||
+ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
|
||||
+
|
||||
+ snd_printdd("atihdmi_playback_pcm_prepare: "
|
||||
+ "NID=0x%x, %shbr-ctl=0x%x\n",
|
||||
+ pin_nid,
|
||||
+ hbr_ctl == hbr_ctl_new ? "" : "new-",
|
||||
+ hbr_ctl_new);
|
||||
+
|
||||
+ if (hbr_ctl != hbr_ctl_new)
|
||||
+ snd_hda_codec_write(codec, pin_nid, 0,
|
||||
+ ATI_VERB_SET_HBR_CONTROL,
|
||||
+ hbr_ctl_new);
|
||||
+
|
||||
+ } else if (is_hbr_format(format)) {
|
||||
+ snd_printdd("atihdmi_playback_pcm_prepare: HBR is not supported\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (is_amdhdmi_rev3(codec)) {
|
||||
+ int ramp_rate = 180; /* default as per spec */
|
||||
+ /* disable ramp-up/down for non-pcm as per spec */
|
||||
+ if (format & AC_FMT_TYPE_NON_PCM)
|
||||
+ ramp_rate = 0;
|
||||
+
|
||||
+ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
|
||||
+ }
|
||||
+
|
||||
+ return generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, format, substream);
|
||||
+}
|
||||
+
|
||||
+static int atihdmi_build_pcms(struct hda_codec *codec)
|
||||
+{
|
||||
+ struct hdmi_spec *spec = codec->spec;
|
||||
+ int err, pin_idx;
|
||||
+
|
||||
+ err = generic_hdmi_build_pcms(codec);
|
||||
+
|
||||
+ if (err)
|
||||
return err;
|
||||
- snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
|
||||
- AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
|
||||
- /* FIXME: XXX */
|
||||
- for (i = 0; i < chans; i++) {
|
||||
- snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
|
||||
- AC_VERB_SET_HDMI_CHAN_SLOT,
|
||||
- (i << 4) | i);
|
||||
+
|
||||
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
+ struct hda_pcm *info = get_pcm_rec(spec, pin_idx);
|
||||
+
|
||||
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].ops.prepare = atihdmi_playback_pcm_prepare;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int atihdmi_init(struct hda_codec *codec)
|
||||
+{
|
||||
+ struct hdmi_spec *spec = codec->spec;
|
||||
+ int pin_idx, err;
|
||||
+
|
||||
+ err = generic_hdmi_init(codec);
|
||||
+
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
+
|
||||
+ /* make sure downmix information in infoframe is zero */
|
||||
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
|
||||
+
|
||||
+ /* enable channel-wise remap mode if supported */
|
||||
+ if (has_amd_full_remap_support(codec))
|
||||
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_MULTICHANNEL_MODE, 1);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_atihdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
- int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
|
||||
- if (err < 0)
|
||||
+ struct hdmi_spec_per_cvt *per_cvt;
|
||||
+ int err, cvt_idx;
|
||||
+
|
||||
+ err = patch_generic_hdmi(codec);
|
||||
+
|
||||
+ if (err)
|
||||
return err;
|
||||
+
|
||||
+ codec->patch_ops.init = atihdmi_init;
|
||||
+ codec->patch_ops.build_pcms = atihdmi_build_pcms;
|
||||
+
|
||||
+ /* ATI/AMD converters do not advertise all of their capabilities */
|
||||
spec = codec->spec;
|
||||
- spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
|
||||
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
|
||||
+ per_cvt = get_cvt(spec, cvt_idx);
|
||||
+ per_cvt->channels_max = max(per_cvt->channels_max, 8u);
|
||||
+ per_cvt->rates |= SUPPORTED_RATES;
|
||||
+ per_cvt->formats |= SUPPORTED_FORMATS;
|
||||
+ per_cvt->maxbps = max(per_cvt->maxbps, 24u);
|
||||
+ }
|
||||
+
|
||||
+ spec->channels_max = max(spec->channels_max, 8u);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2612,7 +2958,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
||||
{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
|
||||
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi },
|
||||
+{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
|
||||
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
|
@ -1,46 +0,0 @@
|
||||
From 8c01e58073101756ce1aceadf0471fcb0db6a61c Mon Sep 17 00:00:00 2001
|
||||
From: fritsch <peter.fruehberger@gmail.com>
|
||||
Date: Fri, 25 Oct 2013 19:22:44 +0200
|
||||
Subject: [PATCH 2/5] Radeon SI workaround
|
||||
|
||||
---
|
||||
drivers/gpu/drm/radeon/ni.c | 23 ++++++++++++++++++++++-
|
||||
1 file changed, 22 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
|
||||
index cac2866..66441cf 100644
|
||||
--- a/drivers/gpu/drm/radeon/ni.c
|
||||
+++ b/drivers/gpu/drm/radeon/ni.c
|
||||
@@ -933,7 +933,28 @@ static void cayman_gpu_init(struct radeon_device *rdev)
|
||||
rdev->config.cayman.sx_max_export_size = 256;
|
||||
rdev->config.cayman.sx_max_export_pos_size = 64;
|
||||
rdev->config.cayman.sx_max_export_smx_size = 192;
|
||||
- rdev->config.cayman.max_hw_contexts = 8;
|
||||
+ if ((rdev->pdev->device == 0x9900) ||
|
||||
+ (rdev->pdev->device == 0x9901) ||
|
||||
+ (rdev->pdev->device == 0x9903) ||
|
||||
+ (rdev->pdev->device == 0x9904) ||
|
||||
+ (rdev->pdev->device == 0x9905) ||
|
||||
+ (rdev->pdev->device == 0x9906) ||
|
||||
+ (rdev->pdev->device == 0x9907) ||
|
||||
+ (rdev->pdev->device == 0x9908) ||
|
||||
+ (rdev->pdev->device == 0x9909) ||
|
||||
+ (rdev->pdev->device == 0x990A) ||
|
||||
+ (rdev->pdev->device == 0x990B) ||
|
||||
+ (rdev->pdev->device == 0x990C) ||
|
||||
+ (rdev->pdev->device == 0x990D) ||
|
||||
+ (rdev->pdev->device == 0x990E) ||
|
||||
+ (rdev->pdev->device == 0x990F) ||
|
||||
+ (rdev->pdev->device == 0x9910) ||
|
||||
+ (rdev->pdev->device == 0x9913) ||
|
||||
+ (rdev->pdev->device == 0x9917) ||
|
||||
+ (rdev->pdev->device == 0x9918))
|
||||
+ rdev->config.cayman.max_hw_contexts = 8;
|
||||
+ else
|
||||
+ rdev->config.cayman.max_hw_contexts = 4;
|
||||
rdev->config.cayman.sq_num_cf_insts = 2;
|
||||
|
||||
rdev->config.cayman.sc_prim_fifo_size = 0x40;
|
||||
--
|
||||
1.8.3.2
|
||||
|
@ -1,126 +0,0 @@
|
||||
commit 231a66ad6542801caf6a0e624f744d2218f6a6fe
|
||||
Author: fritsch <peter.fruehberger@gmail.com>
|
||||
Date: Tue Oct 29 07:48:53 2013 +0100
|
||||
|
||||
drm/radeon/audio: fix missing multichannel PCM SAD in some cases
|
||||
|
||||
diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c
|
||||
index ab92620..85c4993 100644
|
||||
--- a/drivers/gpu/drm/radeon/dce6_afmt.c
|
||||
+++ b/drivers/gpu/drm/radeon/dce6_afmt.c
|
||||
@@ -244,20 +244,30 @@ void dce6_afmt_write_sad_regs(struct drm_encoder *encoder)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
|
||||
u32 value = 0;
|
||||
+ u8 stereo_freqs = 0;
|
||||
+ int max_channels = -1;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sad_count; j++) {
|
||||
struct cea_sad *sad = &sads[j];
|
||||
|
||||
if (sad->format == eld_reg_to_type[i][1]) {
|
||||
- value = MAX_CHANNELS(sad->channels) |
|
||||
- DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
- SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ if (sad->channels > max_channels) {
|
||||
+ value = MAX_CHANNELS(sad->channels) |
|
||||
+ DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
+ SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ max_channels = sad->channels;
|
||||
+ }
|
||||
+
|
||||
if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
|
||||
- value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
|
||||
- break;
|
||||
+ stereo_freqs |= sad->freq;
|
||||
+ else
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
|
||||
+
|
||||
WREG32_ENDPOINT(offset, eld_reg_to_type[i][0], value);
|
||||
}
|
||||
|
||||
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c
|
||||
index e98b0f3..aa2f4da 100644
|
||||
--- a/drivers/gpu/drm/radeon/evergreen_hdmi.c
|
||||
+++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c
|
||||
@@ -180,20 +180,30 @@ static void evergreen_hdmi_write_sad_regs(struct drm_encoder *encoder)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
|
||||
u32 value = 0;
|
||||
+ u8 stereo_freqs = 0;
|
||||
+ int max_channels = -1;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sad_count; j++) {
|
||||
struct cea_sad *sad = &sads[j];
|
||||
|
||||
if (sad->format == eld_reg_to_type[i][1]) {
|
||||
- value = MAX_CHANNELS(sad->channels) |
|
||||
- DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
- SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ if (sad->channels > max_channels) {
|
||||
+ value = MAX_CHANNELS(sad->channels) |
|
||||
+ DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
+ SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ max_channels = sad->channels;
|
||||
+ }
|
||||
+
|
||||
if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
|
||||
- value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
|
||||
- break;
|
||||
+ stereo_freqs |= sad->freq;
|
||||
+ else
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
|
||||
+
|
||||
WREG32(eld_reg_to_type[i][0], value);
|
||||
}
|
||||
|
||||
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
|
||||
index 06022e3..b90112f 100644
|
||||
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
|
||||
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
|
||||
@@ -384,20 +384,30 @@ static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
|
||||
u32 value = 0;
|
||||
+ u8 stereo_freqs = 0;
|
||||
+ int max_channels = -1;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sad_count; j++) {
|
||||
struct cea_sad *sad = &sads[j];
|
||||
|
||||
if (sad->format == eld_reg_to_type[i][1]) {
|
||||
- value = MAX_CHANNELS(sad->channels) |
|
||||
- DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
- SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ if (sad->channels > max_channels) {
|
||||
+ value = MAX_CHANNELS(sad->channels) |
|
||||
+ DESCRIPTOR_BYTE_2(sad->byte2) |
|
||||
+ SUPPORTED_FREQUENCIES(sad->freq);
|
||||
+ max_channels = sad->channels;
|
||||
+ }
|
||||
+
|
||||
if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
|
||||
- value |= SUPPORTED_FREQUENCIES_STEREO(sad->freq);
|
||||
- break;
|
||||
+ stereo_freqs |= sad->freq;
|
||||
+ else
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
|
||||
+
|
||||
WREG32(eld_reg_to_type[i][0], value);
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
diff -Naur linux-3.10.10/include/uapi/linux/ptrace.h linux-3.10.10.patch/include/uapi/linux/ptrace.h
|
||||
--- linux-3.10.10/include/uapi/linux/ptrace.h 2013-08-29 18:47:51.000000000 +0200
|
||||
+++ linux-3.10.10.patch/include/uapi/linux/ptrace.h 2013-09-04 16:38:10.182685149 +0200
|
||||
@@ -55,11 +55,13 @@
|
||||
|
||||
#define PTRACE_PEEKSIGINFO 0x4209
|
||||
|
||||
+#ifdef __KERNEL__
|
||||
struct ptrace_peeksiginfo_args {
|
||||
__u64 off; /* from which siginfo to start */
|
||||
__u32 flags;
|
||||
__s32 nr; /* how may siginfos to take */
|
||||
};
|
||||
+#endif /* __KERNEL__ */
|
||||
|
||||
/* Read signals from a shared (process wide) queue */
|
||||
#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
|
@ -1,66 +0,0 @@
|
||||
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
|
||||
---
|
||||
drivers/gpu/drm/i915/i915_drv.h | 3 ++-
|
||||
drivers/gpu/drm/i915/intel_display.c | 11 +++++++----
|
||||
2 files changed, 9 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
|
||||
index 6106d3d..caee590 100644
|
||||
--- a/drivers/gpu/drm/i915/i915_drv.h
|
||||
+++ b/drivers/gpu/drm/i915/i915_drv.h
|
||||
@@ -379,7 +379,8 @@ struct drm_i915_display_funcs {
|
||||
void (*crtc_disable)(struct drm_crtc *crtc);
|
||||
void (*off)(struct drm_crtc *crtc);
|
||||
void (*write_eld)(struct drm_connector *connector,
|
||||
- struct drm_crtc *crtc);
|
||||
+ struct drm_crtc *crtc,
|
||||
+ struct drm_display_mode *mode);
|
||||
void (*fdi_link_train)(struct drm_crtc *crtc);
|
||||
void (*init_clock_gating)(struct drm_device *dev);
|
||||
int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
|
||||
index 4f1b636..55740f2 100644
|
||||
--- a/drivers/gpu/drm/i915/intel_display.c
|
||||
+++ b/drivers/gpu/drm/i915/intel_display.c
|
||||
@@ -6752,7 +6752,8 @@ static bool intel_eld_uptodate(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
static void g4x_write_eld(struct drm_connector *connector,
|
||||
- struct drm_crtc *crtc)
|
||||
+ struct drm_crtc *crtc,
|
||||
+ struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
||||
uint8_t *eld = connector->eld;
|
||||
@@ -6792,7 +6793,8 @@ static void g4x_write_eld(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
static void haswell_write_eld(struct drm_connector *connector,
|
||||
- struct drm_crtc *crtc)
|
||||
+ struct drm_crtc *crtc,
|
||||
+ struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
||||
uint8_t *eld = connector->eld;
|
||||
@@ -6879,7 +6881,8 @@ static void haswell_write_eld(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
static void ironlake_write_eld(struct drm_connector *connector,
|
||||
- struct drm_crtc *crtc)
|
||||
+ struct drm_crtc *crtc,
|
||||
+ struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
||||
uint8_t *eld = connector->eld;
|
||||
@@ -6974,7 +6977,7 @@ void intel_write_eld(struct drm_encoder *encoder,
|
||||
connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
|
||||
|
||||
if (dev_priv->display.write_eld)
|
||||
- dev_priv->display.write_eld(connector, crtc);
|
||||
+ dev_priv->display.write_eld(connector, crtc, mode);
|
||||
}
|
||||
|
||||
static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
|
||||
--
|
||||
1.7.9.5
|
||||
|
@ -1,110 +0,0 @@
|
||||
Reference: http://mid.gmane.org/CAGpEb3Ep1LRZETPxHGRfBDqr5Ts2tAc8gCukWwugUf1U5NYv1g@mail.gmail.com
|
||||
Reference: http://mid.gmane.org/20130206213533.GA16367@hardeman.nu
|
||||
Reported-by: David Härdeman <david@hardeman.nu>
|
||||
Reported-by: Jasper Smet <josbeir@gmail.com>
|
||||
Tested-by: Jasper Smet <josbeir@gmail.com>
|
||||
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
|
||||
---
|
||||
drivers/gpu/drm/i915/i915_reg.h | 12 ++++++++-
|
||||
drivers/gpu/drm/i915/intel_display.c | 48 +++++++++++++++++++++++++++++++---
|
||||
2 files changed, 55 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
|
||||
index 13153c3..3266819 100644
|
||||
--- a/drivers/gpu/drm/i915/i915_reg.h
|
||||
+++ b/drivers/gpu/drm/i915/i915_reg.h
|
||||
@@ -4875,7 +4875,17 @@
|
||||
#define AUD_CONFIG_LOWER_N_SHIFT 4
|
||||
#define AUD_CONFIG_LOWER_N_VALUE (0xfff << 4)
|
||||
#define AUD_CONFIG_PIXEL_CLOCK_HDMI_SHIFT 16
|
||||
-#define AUD_CONFIG_PIXEL_CLOCK_HDMI (0xf << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK (0xf << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_25175 (0 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_25200 (1 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_27000 (2 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_27027 (3 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_54000 (4 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_54054 (5 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_74176 (6 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_74250 (7 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_148352 (8 << 16)
|
||||
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 (9 << 16)
|
||||
#define AUD_CONFIG_DISABLE_NCTS (1 << 3)
|
||||
|
||||
/* HSW Audio */
|
||||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
|
||||
index 55740f2..a097f84 100644
|
||||
--- a/drivers/gpu/drm/i915/intel_display.c
|
||||
+++ b/drivers/gpu/drm/i915/intel_display.c
|
||||
@@ -6722,6 +6722,44 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static struct {
|
||||
+ int clock;
|
||||
+ u32 config;
|
||||
+} hdmi_audio_clock[] = {
|
||||
+ { DIV_ROUND_UP(25200 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_25175 },
|
||||
+ { 25200, AUD_CONFIG_PIXEL_CLOCK_HDMI_25200 }, /* default per bspec */
|
||||
+ { 27000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27000 },
|
||||
+ { 27000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27027 },
|
||||
+ { 54000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54000 },
|
||||
+ { 54000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54054 },
|
||||
+ { DIV_ROUND_UP(74250 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_74176 },
|
||||
+ { 74250, AUD_CONFIG_PIXEL_CLOCK_HDMI_74250 },
|
||||
+ { DIV_ROUND_UP(148500 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_148352 },
|
||||
+ { 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
|
||||
+};
|
||||
+
|
||||
+/* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
|
||||
+static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(hdmi_audio_clock); i++) {
|
||||
+ if (mode->clock == hdmi_audio_clock[i].clock)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (i == ARRAY_SIZE(hdmi_audio_clock)) {
|
||||
+ DRM_DEBUG_KMS("HDMI audio pixel clock setting for %d not found, falling back to defaults\n", mode->clock);
|
||||
+ i = 1;
|
||||
+ }
|
||||
+
|
||||
+ DRM_DEBUG_KMS("Configuring HDMI audio for pixel clock %d (0x%08x)\n",
|
||||
+ hdmi_audio_clock[i].clock,
|
||||
+ hdmi_audio_clock[i].config);
|
||||
+
|
||||
+ return hdmi_audio_clock[i].config;
|
||||
+}
|
||||
+
|
||||
static bool intel_eld_uptodate(struct drm_connector *connector,
|
||||
int reg_eldv, uint32_t bits_eldv,
|
||||
int reg_elda, uint32_t bits_elda,
|
||||
@@ -6847,8 +6885,9 @@ static void haswell_write_eld(struct drm_connector *connector,
|
||||
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
|
||||
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
|
||||
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
|
||||
- } else
|
||||
- I915_WRITE(aud_config, 0);
|
||||
+ } else {
|
||||
+ I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
|
||||
+ }
|
||||
|
||||
if (intel_eld_uptodate(connector,
|
||||
aud_cntrl_st2, eldv,
|
||||
@@ -6926,8 +6965,9 @@ static void ironlake_write_eld(struct drm_connector *connector,
|
||||
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
|
||||
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
|
||||
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
|
||||
- } else
|
||||
- I915_WRITE(aud_config, 0);
|
||||
+ } else {
|
||||
+ I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
|
||||
+ }
|
||||
|
||||
if (intel_eld_uptodate(connector,
|
||||
aud_cntrl_st2, eldv,
|
||||
--
|
||||
1.7.9.5
|
||||
|
@ -1,51 +0,0 @@
|
||||
From fab1285a51b7bf55adb4678d82e606829c9dab85 Mon Sep 17 00:00:00 2001
|
||||
From: Takashi Iwai <tiwai@suse.de>
|
||||
Date: Tue, 5 Nov 2013 17:54:05 +0100
|
||||
Subject: [PATCH] ALSA: hda - Name Haswell HDMI controllers better
|
||||
|
||||
"HDA Intel MID" is no correct name for Haswell HDMI controllers.
|
||||
Give them a better name, "HDA Intel HDMI".
|
||||
|
||||
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
||||
---
|
||||
sound/pci/hda/hda_intel.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
|
||||
index 9cbd125..17f4aa8 100644
|
||||
--- a/sound/pci/hda/hda_intel.c
|
||||
+++ b/sound/pci/hda/hda_intel.c
|
||||
@@ -569,6 +569,7 @@ enum {
|
||||
AZX_DRIVER_ICH,
|
||||
AZX_DRIVER_PCH,
|
||||
AZX_DRIVER_SCH,
|
||||
+ AZX_DRIVER_HDMI,
|
||||
AZX_DRIVER_ATI,
|
||||
AZX_DRIVER_ATIHDMI,
|
||||
AZX_DRIVER_ATIHDMI_NS,
|
||||
@@ -648,6 +649,7 @@ static char *driver_short_names[] = {
|
||||
[AZX_DRIVER_ICH] = "HDA Intel",
|
||||
[AZX_DRIVER_PCH] = "HDA Intel PCH",
|
||||
[AZX_DRIVER_SCH] = "HDA Intel MID",
|
||||
+ [AZX_DRIVER_HDMI] = "HDA Intel HDMI",
|
||||
[AZX_DRIVER_ATI] = "HDA ATI SB",
|
||||
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
|
||||
[AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
|
||||
@@ -3997,11 +3999,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
|
||||
/* Haswell */
|
||||
{ PCI_DEVICE(0x8086, 0x0a0c),
|
||||
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL },
|
||||
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
|
||||
{ PCI_DEVICE(0x8086, 0x0c0c),
|
||||
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL },
|
||||
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
|
||||
{ PCI_DEVICE(0x8086, 0x0d0c),
|
||||
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_HASWELL },
|
||||
+ .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
|
||||
/* 5 Series/3400 */
|
||||
{ PCI_DEVICE(0x8086, 0x3b56),
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
|
||||
--
|
||||
1.7.9.5
|
||||
|
@ -1,732 +0,0 @@
|
||||
From 9508c6b90b3f57ecea4e7a896cf8325400fc0c6e Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Wed, 13 Nov 2013 02:56:26 +0000
|
||||
Subject: [PATCH 1/7] Squashfs: Refactor decompressor interface and code
|
||||
|
||||
The decompressor interface and code was written from
|
||||
the point of view of single-threaded operation. In doing
|
||||
so it mixed a lot of single-threaded implementation specific
|
||||
aspects into the decompressor code and elsewhere which makes it
|
||||
difficult to seamlessly support multiple different decompressor
|
||||
implementations.
|
||||
|
||||
This patch does the following:
|
||||
|
||||
1. It removes compressor_options parsing from the decompressor
|
||||
init() function. This allows the decompressor init() function
|
||||
to be dynamically called to instantiate multiple decompressors,
|
||||
without the compressor options needing to be read and parsed each
|
||||
time.
|
||||
|
||||
2. It moves threading and all sleeping operations out of the
|
||||
decompressors. In doing so, it makes the decompressors
|
||||
non-blocking wrappers which only deal with interfacing with
|
||||
the decompressor implementation.
|
||||
|
||||
3. It splits decompressor.[ch] into decompressor generic functions
|
||||
in decompressor.[ch], and moves the single threaded
|
||||
decompressor implementation into decompressor_single.c.
|
||||
|
||||
The result of this patch is Squashfs should now be able to
|
||||
support multiple decompressors by adding new decompressor_xxx.c
|
||||
files with specialised implementations of the functions in
|
||||
decompressor_single.c
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Reviewed-by: Minchan Kim <minchan@kernel.org>
|
||||
---
|
||||
fs/squashfs/Makefile | 2 +-
|
||||
fs/squashfs/block.c | 11 +++--
|
||||
fs/squashfs/decompressor.c | 47 +++++++++++++-------
|
||||
fs/squashfs/decompressor.h | 21 +++------
|
||||
fs/squashfs/decompressor_single.c | 86 +++++++++++++++++++++++++++++++++++
|
||||
fs/squashfs/lzo_wrapper.c | 24 +++-------
|
||||
fs/squashfs/squashfs.h | 9 +++-
|
||||
fs/squashfs/squashfs_fs_sb.h | 3 +-
|
||||
fs/squashfs/super.c | 10 ++---
|
||||
fs/squashfs/xz_wrapper.c | 89 ++++++++++++++++++++-----------------
|
||||
fs/squashfs/zlib_wrapper.c | 50 +++++++--------------
|
||||
11 files changed, 216 insertions(+), 136 deletions(-)
|
||||
create mode 100644 fs/squashfs/decompressor_single.c
|
||||
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index 110b047..c223c84 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
-squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
+squashfs-y += namei.o super.o symlink.o decompressor.o decompressor_single.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
|
||||
squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
|
||||
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
|
||||
index 41d108e..4dd4025 100644
|
||||
--- a/fs/squashfs/block.c
|
||||
+++ b/fs/squashfs/block.c
|
||||
@@ -93,7 +93,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
struct buffer_head **bh;
|
||||
int offset = index & ((1 << msblk->devblksize_log2) - 1);
|
||||
u64 cur_index = index >> msblk->devblksize_log2;
|
||||
- int bytes, compressed, b = 0, k = 0, page = 0, avail;
|
||||
+ int bytes, compressed, b = 0, k = 0, page = 0, avail, i;
|
||||
|
||||
bh = kcalloc(((srclength + msblk->devblksize - 1)
|
||||
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
|
||||
@@ -158,6 +158,12 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
ll_rw_block(READ, b - 1, bh + 1);
|
||||
}
|
||||
|
||||
+ for (i = 0; i < b; i++) {
|
||||
+ wait_on_buffer(bh[i]);
|
||||
+ if (!buffer_uptodate(bh[i]))
|
||||
+ goto block_release;
|
||||
+ }
|
||||
+
|
||||
if (compressed) {
|
||||
length = squashfs_decompress(msblk, buffer, bh, b, offset,
|
||||
length, srclength, pages);
|
||||
@@ -172,9 +178,6 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
for (bytes = length; k < b; k++) {
|
||||
in = min(bytes, msblk->devblksize - offset);
|
||||
bytes -= in;
|
||||
- wait_on_buffer(bh[k]);
|
||||
- if (!buffer_uptodate(bh[k]))
|
||||
- goto block_release;
|
||||
while (in) {
|
||||
if (pg_offset == PAGE_CACHE_SIZE) {
|
||||
page++;
|
||||
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
|
||||
index 3f6271d..234291f 100644
|
||||
--- a/fs/squashfs/decompressor.c
|
||||
+++ b/fs/squashfs/decompressor.c
|
||||
@@ -37,29 +37,29 @@
|
||||
*/
|
||||
|
||||
static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
|
||||
- NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
|
||||
+ NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
|
||||
};
|
||||
|
||||
#ifndef CONFIG_SQUASHFS_LZO
|
||||
static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
|
||||
- NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
|
||||
+ NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SQUASHFS_XZ
|
||||
static const struct squashfs_decompressor squashfs_xz_comp_ops = {
|
||||
- NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
|
||||
+ NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SQUASHFS_ZLIB
|
||||
static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
|
||||
- NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
|
||||
+ NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
|
||||
- NULL, NULL, NULL, 0, "unknown", 0
|
||||
+ NULL, NULL, NULL, NULL, 0, "unknown", 0
|
||||
};
|
||||
|
||||
static const struct squashfs_decompressor *decompressor[] = {
|
||||
@@ -83,10 +83,10 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
|
||||
}
|
||||
|
||||
|
||||
-void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
|
||||
+static void *get_comp_opts(struct super_block *sb, unsigned short flags)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
- void *strm, *buffer = NULL;
|
||||
+ void *buffer = NULL, *comp_opts;
|
||||
int length = 0;
|
||||
|
||||
/*
|
||||
@@ -94,23 +94,40 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
|
||||
*/
|
||||
if (SQUASHFS_COMP_OPTS(flags)) {
|
||||
buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
|
||||
- if (buffer == NULL)
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
+ if (buffer == NULL) {
|
||||
+ comp_opts = ERR_PTR(-ENOMEM);
|
||||
+ goto out;
|
||||
+ }
|
||||
|
||||
length = squashfs_read_data(sb, &buffer,
|
||||
sizeof(struct squashfs_super_block), 0, NULL,
|
||||
- PAGE_CACHE_SIZE, 1);
|
||||
+ PAGE_CACHE_SIZE, 1);
|
||||
|
||||
if (length < 0) {
|
||||
- strm = ERR_PTR(length);
|
||||
- goto finished;
|
||||
+ comp_opts = ERR_PTR(length);
|
||||
+ goto out;
|
||||
}
|
||||
}
|
||||
|
||||
- strm = msblk->decompressor->init(msblk, buffer, length);
|
||||
+ comp_opts = squashfs_comp_opts(msblk, buffer, length);
|
||||
|
||||
-finished:
|
||||
+out:
|
||||
kfree(buffer);
|
||||
+ return comp_opts;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
|
||||
+{
|
||||
+ struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
+ void *stream, *comp_opts = get_comp_opts(sb, flags);
|
||||
+
|
||||
+ if (IS_ERR(comp_opts))
|
||||
+ return comp_opts;
|
||||
+
|
||||
+ stream = squashfs_decompressor_create(msblk, comp_opts);
|
||||
+ if (IS_ERR(stream))
|
||||
+ kfree(comp_opts);
|
||||
|
||||
- return strm;
|
||||
+ return stream;
|
||||
}
|
||||
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
|
||||
index 330073e..6cdb20a 100644
|
||||
--- a/fs/squashfs/decompressor.h
|
||||
+++ b/fs/squashfs/decompressor.h
|
||||
@@ -24,28 +24,21 @@
|
||||
*/
|
||||
|
||||
struct squashfs_decompressor {
|
||||
- void *(*init)(struct squashfs_sb_info *, void *, int);
|
||||
+ void *(*init)(struct squashfs_sb_info *, void *);
|
||||
+ void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
|
||||
void (*free)(void *);
|
||||
- int (*decompress)(struct squashfs_sb_info *, void **,
|
||||
+ int (*decompress)(struct squashfs_sb_info *, void *, void **,
|
||||
struct buffer_head **, int, int, int, int, int);
|
||||
int id;
|
||||
char *name;
|
||||
int supported;
|
||||
};
|
||||
|
||||
-static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
|
||||
- void *s)
|
||||
+static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
|
||||
+ void *buff, int length)
|
||||
{
|
||||
- if (msblk->decompressor)
|
||||
- msblk->decompressor->free(s);
|
||||
-}
|
||||
-
|
||||
-static inline int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
-{
|
||||
- return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
|
||||
- length, srclength, pages);
|
||||
+ return msblk->decompressor->comp_opts ?
|
||||
+ msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SQUASHFS_XZ
|
||||
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
|
||||
new file mode 100644
|
||||
index 0000000..f857cf6
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/decompressor_single.c
|
||||
@@ -0,0 +1,86 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/buffer_head.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "decompressor.h"
|
||||
+#include "squashfs.h"
|
||||
+
|
||||
+/*
|
||||
+ * This file implements single-threaded decompression in the
|
||||
+ * decompressor framework
|
||||
+ */
|
||||
+
|
||||
+struct squashfs_stream {
|
||||
+ void *stream;
|
||||
+ struct mutex mutex;
|
||||
+};
|
||||
+
|
||||
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
|
||||
+ void *comp_opts)
|
||||
+{
|
||||
+ struct squashfs_stream *stream;
|
||||
+ int err = -ENOMEM;
|
||||
+
|
||||
+ stream = kmalloc(sizeof(*stream), GFP_KERNEL);
|
||||
+ if (stream == NULL)
|
||||
+ goto out;
|
||||
+
|
||||
+ stream->stream = msblk->decompressor->init(msblk, comp_opts);
|
||||
+ if (IS_ERR(stream->stream)) {
|
||||
+ err = PTR_ERR(stream->stream);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ kfree(comp_opts);
|
||||
+ mutex_init(&stream->mutex);
|
||||
+ return stream;
|
||||
+
|
||||
+out:
|
||||
+ kfree(stream);
|
||||
+ return ERR_PTR(err);
|
||||
+}
|
||||
+
|
||||
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
+{
|
||||
+ struct squashfs_stream *stream = msblk->stream;
|
||||
+
|
||||
+ if (stream) {
|
||||
+ msblk->decompressor->free(stream->stream);
|
||||
+ kfree(stream);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
+{
|
||||
+ int res;
|
||||
+ struct squashfs_stream *stream = msblk->stream;
|
||||
+
|
||||
+ mutex_lock(&stream->mutex);
|
||||
+ res = msblk->decompressor->decompress(msblk, stream->stream, buffer,
|
||||
+ bh, b, offset, length, srclength, pages);
|
||||
+ mutex_unlock(&stream->mutex);
|
||||
+
|
||||
+ if (res < 0)
|
||||
+ ERROR("%s decompression failed, data probably corrupt\n",
|
||||
+ msblk->decompressor->name);
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+int squashfs_max_decompressors(void)
|
||||
+{
|
||||
+ return 1;
|
||||
+}
|
||||
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
|
||||
index 00f4dfc..75c3b57 100644
|
||||
--- a/fs/squashfs/lzo_wrapper.c
|
||||
+++ b/fs/squashfs/lzo_wrapper.c
|
||||
@@ -37,7 +37,7 @@ struct squashfs_lzo {
|
||||
void *output;
|
||||
};
|
||||
|
||||
-static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
|
||||
+static void *lzo_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
{
|
||||
int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
|
||||
|
||||
@@ -74,22 +74,16 @@ static void lzo_free(void *strm)
|
||||
}
|
||||
|
||||
|
||||
-static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
- struct buffer_head **bh, int b, int offset, int length, int srclength,
|
||||
- int pages)
|
||||
+static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
{
|
||||
- struct squashfs_lzo *stream = msblk->stream;
|
||||
+ struct squashfs_lzo *stream = strm;
|
||||
void *buff = stream->input;
|
||||
int avail, i, bytes = length, res;
|
||||
size_t out_len = srclength;
|
||||
|
||||
- mutex_lock(&msblk->read_data_mutex);
|
||||
-
|
||||
for (i = 0; i < b; i++) {
|
||||
- wait_on_buffer(bh[i]);
|
||||
- if (!buffer_uptodate(bh[i]))
|
||||
- goto block_release;
|
||||
-
|
||||
avail = min(bytes, msblk->devblksize - offset);
|
||||
memcpy(buff, bh[i]->b_data + offset, avail);
|
||||
buff += avail;
|
||||
@@ -111,17 +105,9 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
bytes -= avail;
|
||||
}
|
||||
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
return res;
|
||||
|
||||
-block_release:
|
||||
- for (; i < b; i++)
|
||||
- put_bh(bh[i]);
|
||||
-
|
||||
failed:
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
-
|
||||
- ERROR("lzo decompression failed, data probably corrupt\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
|
||||
index d126651..2e2751d 100644
|
||||
--- a/fs/squashfs/squashfs.h
|
||||
+++ b/fs/squashfs/squashfs.h
|
||||
@@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int);
|
||||
|
||||
/* decompressor.c */
|
||||
extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
|
||||
-extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
|
||||
+extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
|
||||
+
|
||||
+/* decompressor_xxx.c */
|
||||
+extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
|
||||
+extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
|
||||
+extern int squashfs_decompress(struct squashfs_sb_info *, void **,
|
||||
+ struct buffer_head **, int, int, int, int, int);
|
||||
+extern int squashfs_max_decompressors(void);
|
||||
|
||||
/* export.c */
|
||||
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
|
||||
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
|
||||
index 52934a2..9cdcf41 100644
|
||||
--- a/fs/squashfs/squashfs_fs_sb.h
|
||||
+++ b/fs/squashfs/squashfs_fs_sb.h
|
||||
@@ -63,10 +63,9 @@ struct squashfs_sb_info {
|
||||
__le64 *id_table;
|
||||
__le64 *fragment_index;
|
||||
__le64 *xattr_id_table;
|
||||
- struct mutex read_data_mutex;
|
||||
struct mutex meta_index_mutex;
|
||||
struct meta_index *meta_index;
|
||||
- void *stream;
|
||||
+ struct squashfs_stream *stream;
|
||||
__le64 *inode_lookup_table;
|
||||
u64 inode_table;
|
||||
u64 directory_table;
|
||||
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
|
||||
index 60553a9..202df63 100644
|
||||
--- a/fs/squashfs/super.c
|
||||
+++ b/fs/squashfs/super.c
|
||||
@@ -98,7 +98,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE);
|
||||
msblk->devblksize_log2 = ffz(~msblk->devblksize);
|
||||
|
||||
- mutex_init(&msblk->read_data_mutex);
|
||||
mutex_init(&msblk->meta_index_mutex);
|
||||
|
||||
/*
|
||||
@@ -206,13 +205,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto failed_mount;
|
||||
|
||||
/* Allocate read_page block */
|
||||
- msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
|
||||
+ msblk->read_page = squashfs_cache_init("data",
|
||||
+ squashfs_max_decompressors(), msblk->block_size);
|
||||
if (msblk->read_page == NULL) {
|
||||
ERROR("Failed to allocate read_page block\n");
|
||||
goto failed_mount;
|
||||
}
|
||||
|
||||
- msblk->stream = squashfs_decompressor_init(sb, flags);
|
||||
+ msblk->stream = squashfs_decompressor_setup(sb, flags);
|
||||
if (IS_ERR(msblk->stream)) {
|
||||
err = PTR_ERR(msblk->stream);
|
||||
msblk->stream = NULL;
|
||||
@@ -336,7 +336,7 @@ failed_mount:
|
||||
squashfs_cache_delete(msblk->block_cache);
|
||||
squashfs_cache_delete(msblk->fragment_cache);
|
||||
squashfs_cache_delete(msblk->read_page);
|
||||
- squashfs_decompressor_free(msblk, msblk->stream);
|
||||
+ squashfs_decompressor_destroy(msblk);
|
||||
kfree(msblk->inode_lookup_table);
|
||||
kfree(msblk->fragment_index);
|
||||
kfree(msblk->id_table);
|
||||
@@ -383,7 +383,7 @@ static void squashfs_put_super(struct super_block *sb)
|
||||
squashfs_cache_delete(sbi->block_cache);
|
||||
squashfs_cache_delete(sbi->fragment_cache);
|
||||
squashfs_cache_delete(sbi->read_page);
|
||||
- squashfs_decompressor_free(sbi, sbi->stream);
|
||||
+ squashfs_decompressor_destroy(sbi);
|
||||
kfree(sbi->id_table);
|
||||
kfree(sbi->fragment_index);
|
||||
kfree(sbi->meta_index);
|
||||
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
|
||||
index 1760b7d1..5d1d07c 100644
|
||||
--- a/fs/squashfs/xz_wrapper.c
|
||||
+++ b/fs/squashfs/xz_wrapper.c
|
||||
@@ -38,38 +38,63 @@ struct squashfs_xz {
|
||||
struct xz_buf buf;
|
||||
};
|
||||
|
||||
-struct comp_opts {
|
||||
+struct disk_comp_opts {
|
||||
__le32 dictionary_size;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
-static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
|
||||
- int len)
|
||||
+struct comp_opts {
|
||||
+ int dict_size;
|
||||
+};
|
||||
+
|
||||
+static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
|
||||
+ void *buff, int len)
|
||||
{
|
||||
- struct comp_opts *comp_opts = buff;
|
||||
- struct squashfs_xz *stream;
|
||||
- int dict_size = msblk->block_size;
|
||||
- int err, n;
|
||||
+ struct disk_comp_opts *comp_opts = buff;
|
||||
+ struct comp_opts *opts;
|
||||
+ int err = 0, n;
|
||||
+
|
||||
+ opts = kmalloc(sizeof(*opts), GFP_KERNEL);
|
||||
+ if (opts == NULL) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto out2;
|
||||
+ }
|
||||
|
||||
if (comp_opts) {
|
||||
/* check compressor options are the expected length */
|
||||
if (len < sizeof(*comp_opts)) {
|
||||
err = -EIO;
|
||||
- goto failed;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
- dict_size = le32_to_cpu(comp_opts->dictionary_size);
|
||||
+ opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
|
||||
|
||||
/* the dictionary size should be 2^n or 2^n+2^(n+1) */
|
||||
- n = ffs(dict_size) - 1;
|
||||
- if (dict_size != (1 << n) && dict_size != (1 << n) +
|
||||
+ n = ffs(opts->dict_size) - 1;
|
||||
+ if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) +
|
||||
(1 << (n + 1))) {
|
||||
err = -EIO;
|
||||
- goto failed;
|
||||
+ goto out;
|
||||
}
|
||||
- }
|
||||
+ } else
|
||||
+ /* use defaults */
|
||||
+ opts->dict_size = max_t(int, msblk->block_size,
|
||||
+ SQUASHFS_METADATA_SIZE);
|
||||
|
||||
- dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
|
||||
+ return opts;
|
||||
+
|
||||
+out:
|
||||
+ kfree(opts);
|
||||
+out2:
|
||||
+ return ERR_PTR(err);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
|
||||
+{
|
||||
+ struct comp_opts *comp_opts = buff;
|
||||
+ struct squashfs_xz *stream;
|
||||
+ int err;
|
||||
|
||||
stream = kmalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (stream == NULL) {
|
||||
@@ -77,7 +102,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
- stream->state = xz_dec_init(XZ_PREALLOC, dict_size);
|
||||
+ stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
|
||||
if (stream->state == NULL) {
|
||||
kfree(stream);
|
||||
err = -ENOMEM;
|
||||
@@ -103,15 +128,13 @@ static void squashfs_xz_free(void *strm)
|
||||
}
|
||||
|
||||
|
||||
-static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
- struct buffer_head **bh, int b, int offset, int length, int srclength,
|
||||
- int pages)
|
||||
+static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
{
|
||||
enum xz_ret xz_err;
|
||||
int avail, total = 0, k = 0, page = 0;
|
||||
- struct squashfs_xz *stream = msblk->stream;
|
||||
-
|
||||
- mutex_lock(&msblk->read_data_mutex);
|
||||
+ struct squashfs_xz *stream = strm;
|
||||
|
||||
xz_dec_reset(stream->state);
|
||||
stream->buf.in_pos = 0;
|
||||
@@ -124,10 +147,6 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
if (stream->buf.in_pos == stream->buf.in_size && k < b) {
|
||||
avail = min(length, msblk->devblksize - offset);
|
||||
length -= avail;
|
||||
- wait_on_buffer(bh[k]);
|
||||
- if (!buffer_uptodate(bh[k]))
|
||||
- goto release_mutex;
|
||||
-
|
||||
stream->buf.in = bh[k]->b_data + offset;
|
||||
stream->buf.in_size = avail;
|
||||
stream->buf.in_pos = 0;
|
||||
@@ -147,23 +166,12 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
put_bh(bh[k++]);
|
||||
} while (xz_err == XZ_OK);
|
||||
|
||||
- if (xz_err != XZ_STREAM_END) {
|
||||
- ERROR("xz_dec_run error, data probably corrupt\n");
|
||||
- goto release_mutex;
|
||||
- }
|
||||
-
|
||||
- if (k < b) {
|
||||
- ERROR("xz_uncompress error, input remaining\n");
|
||||
- goto release_mutex;
|
||||
- }
|
||||
-
|
||||
- total += stream->buf.out_pos;
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
- return total;
|
||||
+ if (xz_err != XZ_STREAM_END || k < b)
|
||||
+ goto out;
|
||||
|
||||
-release_mutex:
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
+ return total + stream->buf.out_pos;
|
||||
|
||||
+out:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
|
||||
@@ -172,6 +180,7 @@ release_mutex:
|
||||
|
||||
const struct squashfs_decompressor squashfs_xz_comp_ops = {
|
||||
.init = squashfs_xz_init,
|
||||
+ .comp_opts = squashfs_xz_comp_opts,
|
||||
.free = squashfs_xz_free,
|
||||
.decompress = squashfs_xz_uncompress,
|
||||
.id = XZ_COMPRESSION,
|
||||
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
|
||||
index 55d918f..bb04902 100644
|
||||
--- a/fs/squashfs/zlib_wrapper.c
|
||||
+++ b/fs/squashfs/zlib_wrapper.c
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
|
||||
-static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
|
||||
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
|
||||
{
|
||||
z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
|
||||
if (stream == NULL)
|
||||
@@ -61,15 +61,13 @@ static void zlib_free(void *strm)
|
||||
}
|
||||
|
||||
|
||||
-static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
- struct buffer_head **bh, int b, int offset, int length, int srclength,
|
||||
- int pages)
|
||||
+static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
{
|
||||
int zlib_err, zlib_init = 0;
|
||||
int k = 0, page = 0;
|
||||
- z_stream *stream = msblk->stream;
|
||||
-
|
||||
- mutex_lock(&msblk->read_data_mutex);
|
||||
+ z_stream *stream = strm;
|
||||
|
||||
stream->avail_out = 0;
|
||||
stream->avail_in = 0;
|
||||
@@ -78,10 +76,6 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
if (stream->avail_in == 0 && k < b) {
|
||||
int avail = min(length, msblk->devblksize - offset);
|
||||
length -= avail;
|
||||
- wait_on_buffer(bh[k]);
|
||||
- if (!buffer_uptodate(bh[k]))
|
||||
- goto release_mutex;
|
||||
-
|
||||
stream->next_in = bh[k]->b_data + offset;
|
||||
stream->avail_in = avail;
|
||||
offset = 0;
|
||||
@@ -94,12 +88,8 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
|
||||
if (!zlib_init) {
|
||||
zlib_err = zlib_inflateInit(stream);
|
||||
- if (zlib_err != Z_OK) {
|
||||
- ERROR("zlib_inflateInit returned unexpected "
|
||||
- "result 0x%x, srclength %d\n",
|
||||
- zlib_err, srclength);
|
||||
- goto release_mutex;
|
||||
- }
|
||||
+ if (zlib_err != Z_OK)
|
||||
+ goto out;
|
||||
zlib_init = 1;
|
||||
}
|
||||
|
||||
@@ -109,29 +99,19 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
|
||||
put_bh(bh[k++]);
|
||||
} while (zlib_err == Z_OK);
|
||||
|
||||
- if (zlib_err != Z_STREAM_END) {
|
||||
- ERROR("zlib_inflate error, data probably corrupt\n");
|
||||
- goto release_mutex;
|
||||
- }
|
||||
+ if (zlib_err != Z_STREAM_END)
|
||||
+ goto out;
|
||||
|
||||
zlib_err = zlib_inflateEnd(stream);
|
||||
- if (zlib_err != Z_OK) {
|
||||
- ERROR("zlib_inflate error, data probably corrupt\n");
|
||||
- goto release_mutex;
|
||||
- }
|
||||
-
|
||||
- if (k < b) {
|
||||
- ERROR("zlib_uncompress error, data remaining\n");
|
||||
- goto release_mutex;
|
||||
- }
|
||||
+ if (zlib_err != Z_OK)
|
||||
+ goto out;
|
||||
|
||||
- length = stream->total_out;
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
- return length;
|
||||
+ if (k < b)
|
||||
+ goto out;
|
||||
|
||||
-release_mutex:
|
||||
- mutex_unlock(&msblk->read_data_mutex);
|
||||
+ return stream->total_out;
|
||||
|
||||
+out:
|
||||
for (; k < b; k++)
|
||||
put_bh(bh[k]);
|
||||
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,285 +0,0 @@
|
||||
From cd59c2ec5f37a2bc1315c9324aab6c21d43ffa1a Mon Sep 17 00:00:00 2001
|
||||
From: Minchan Kim <minchan@kernel.org>
|
||||
Date: Mon, 28 Oct 2013 14:26:30 +0900
|
||||
Subject: [PATCH 2/7] squashfs: Enhance parallel I/O
|
||||
|
||||
Now squashfs have used for only one stream buffer for decompression
|
||||
so it hurts parallel read performance so this patch supports
|
||||
multiple decompressor to enhance performance parallel I/O.
|
||||
|
||||
Four 1G file dd read on KVM machine which has 2 CPU and 4G memory.
|
||||
|
||||
dd if=test/test1.dat of=/dev/null &
|
||||
dd if=test/test2.dat of=/dev/null &
|
||||
dd if=test/test3.dat of=/dev/null &
|
||||
dd if=test/test4.dat of=/dev/null &
|
||||
|
||||
old : 1m39s -> new : 9s
|
||||
|
||||
* From v1
|
||||
* Change comp_strm with decomp_strm - Phillip
|
||||
* Change/add comments - Phillip
|
||||
|
||||
Signed-off-by: Minchan Kim <minchan@kernel.org>
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
---
|
||||
fs/squashfs/Kconfig | 13 +++
|
||||
fs/squashfs/Makefile | 9 +-
|
||||
fs/squashfs/decompressor_multi.c | 200 ++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 221 insertions(+), 1 deletion(-)
|
||||
create mode 100644 fs/squashfs/decompressor_multi.c
|
||||
|
||||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
|
||||
index c70111e..1c6d340 100644
|
||||
--- a/fs/squashfs/Kconfig
|
||||
+++ b/fs/squashfs/Kconfig
|
||||
@@ -63,6 +63,19 @@ config SQUASHFS_LZO
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
+config SQUASHFS_MULTI_DECOMPRESSOR
|
||||
+ bool "Use multiple decompressors for handling parallel I/O"
|
||||
+ depends on SQUASHFS
|
||||
+ help
|
||||
+ By default Squashfs uses a single decompressor but it gives
|
||||
+ poor performance on parallel I/O workloads when using multiple CPU
|
||||
+ machines due to waiting on decompressor availability.
|
||||
+
|
||||
+ If you have a parallel I/O workload and your system has enough memory,
|
||||
+ using this option may improve overall I/O performance.
|
||||
+
|
||||
+ If unsure, say N.
|
||||
+
|
||||
config SQUASHFS_XZ
|
||||
bool "Include support for XZ compressed file systems"
|
||||
depends on SQUASHFS
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index c223c84..dfebc3b 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -4,8 +4,15 @@
|
||||
|
||||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
-squashfs-y += namei.o super.o symlink.o decompressor.o decompressor_single.o
|
||||
+squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
+
|
||||
squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
|
||||
squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o
|
||||
+
|
||||
+ifdef CONFIG_SQUASHFS_MULTI_DECOMPRESSOR
|
||||
+ squashfs-y += decompressor_multi.o
|
||||
+else
|
||||
+ squashfs-y += decompressor_single.o
|
||||
+endif
|
||||
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
|
||||
new file mode 100644
|
||||
index 0000000..462731d
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/decompressor_multi.c
|
||||
@@ -0,0 +1,200 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Minchan Kim <minchan@kernel.org>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/buffer_head.h>
|
||||
+#include <linux/sched.h>
|
||||
+#include <linux/wait.h>
|
||||
+#include <linux/cpumask.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "decompressor.h"
|
||||
+#include "squashfs.h"
|
||||
+
|
||||
+/*
|
||||
+ * This file implements multi-threaded decompression in the
|
||||
+ * decompressor framework
|
||||
+ */
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * The reason that multiply two is that a CPU can request new I/O
|
||||
+ * while it is waiting previous request.
|
||||
+ */
|
||||
+#define MAX_DECOMPRESSOR (num_online_cpus() * 2)
|
||||
+
|
||||
+
|
||||
+int squashfs_max_decompressors(void)
|
||||
+{
|
||||
+ return MAX_DECOMPRESSOR;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+struct squashfs_stream {
|
||||
+ void *comp_opts;
|
||||
+ struct list_head strm_list;
|
||||
+ struct mutex mutex;
|
||||
+ int avail_decomp;
|
||||
+ wait_queue_head_t wait;
|
||||
+};
|
||||
+
|
||||
+
|
||||
+struct decomp_stream {
|
||||
+ void *stream;
|
||||
+ struct list_head list;
|
||||
+};
|
||||
+
|
||||
+
|
||||
+static void put_decomp_stream(struct decomp_stream *decomp_strm,
|
||||
+ struct squashfs_stream *stream)
|
||||
+{
|
||||
+ mutex_lock(&stream->mutex);
|
||||
+ list_add(&decomp_strm->list, &stream->strm_list);
|
||||
+ mutex_unlock(&stream->mutex);
|
||||
+ wake_up(&stream->wait);
|
||||
+}
|
||||
+
|
||||
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
|
||||
+ void *comp_opts)
|
||||
+{
|
||||
+ struct squashfs_stream *stream;
|
||||
+ struct decomp_stream *decomp_strm = NULL;
|
||||
+ int err = -ENOMEM;
|
||||
+
|
||||
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
+ if (!stream)
|
||||
+ goto out;
|
||||
+
|
||||
+ stream->comp_opts = comp_opts;
|
||||
+ mutex_init(&stream->mutex);
|
||||
+ INIT_LIST_HEAD(&stream->strm_list);
|
||||
+ init_waitqueue_head(&stream->wait);
|
||||
+
|
||||
+ /*
|
||||
+ * We should have a decompressor at least as default
|
||||
+ * so if we fail to allocate new decompressor dynamically,
|
||||
+ * we could always fall back to default decompressor and
|
||||
+ * file system works.
|
||||
+ */
|
||||
+ decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
|
||||
+ if (!decomp_strm)
|
||||
+ goto out;
|
||||
+
|
||||
+ decomp_strm->stream = msblk->decompressor->init(msblk,
|
||||
+ stream->comp_opts);
|
||||
+ if (IS_ERR(decomp_strm->stream)) {
|
||||
+ err = PTR_ERR(decomp_strm->stream);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ list_add(&decomp_strm->list, &stream->strm_list);
|
||||
+ stream->avail_decomp = 1;
|
||||
+ return stream;
|
||||
+
|
||||
+out:
|
||||
+ kfree(decomp_strm);
|
||||
+ kfree(stream);
|
||||
+ return ERR_PTR(err);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
+{
|
||||
+ struct squashfs_stream *stream = msblk->stream;
|
||||
+ if (stream) {
|
||||
+ struct decomp_stream *decomp_strm;
|
||||
+
|
||||
+ while (!list_empty(&stream->strm_list)) {
|
||||
+ decomp_strm = list_entry(stream->strm_list.prev,
|
||||
+ struct decomp_stream, list);
|
||||
+ list_del(&decomp_strm->list);
|
||||
+ msblk->decompressor->free(decomp_strm->stream);
|
||||
+ kfree(decomp_strm);
|
||||
+ stream->avail_decomp--;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ WARN_ON(stream->avail_decomp);
|
||||
+ kfree(stream->comp_opts);
|
||||
+ kfree(stream);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk,
|
||||
+ struct squashfs_stream *stream)
|
||||
+{
|
||||
+ struct decomp_stream *decomp_strm;
|
||||
+
|
||||
+ while (1) {
|
||||
+ mutex_lock(&stream->mutex);
|
||||
+
|
||||
+ /* There is available decomp_stream */
|
||||
+ if (!list_empty(&stream->strm_list)) {
|
||||
+ decomp_strm = list_entry(stream->strm_list.prev,
|
||||
+ struct decomp_stream, list);
|
||||
+ list_del(&decomp_strm->list);
|
||||
+ mutex_unlock(&stream->mutex);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * If there is no available decomp and already full,
|
||||
+ * let's wait for releasing decomp from other users.
|
||||
+ */
|
||||
+ if (stream->avail_decomp >= MAX_DECOMPRESSOR)
|
||||
+ goto wait;
|
||||
+
|
||||
+ /* Let's allocate new decomp */
|
||||
+ decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL);
|
||||
+ if (!decomp_strm)
|
||||
+ goto wait;
|
||||
+
|
||||
+ decomp_strm->stream = msblk->decompressor->init(msblk,
|
||||
+ stream->comp_opts);
|
||||
+ if (IS_ERR(decomp_strm->stream)) {
|
||||
+ kfree(decomp_strm);
|
||||
+ goto wait;
|
||||
+ }
|
||||
+
|
||||
+ stream->avail_decomp++;
|
||||
+ WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR);
|
||||
+
|
||||
+ mutex_unlock(&stream->mutex);
|
||||
+ break;
|
||||
+wait:
|
||||
+ /*
|
||||
+ * If system memory is tough, let's for other's
|
||||
+ * releasing instead of hurting VM because it could
|
||||
+ * make page cache thrashing.
|
||||
+ */
|
||||
+ mutex_unlock(&stream->mutex);
|
||||
+ wait_event(stream->wait,
|
||||
+ !list_empty(&stream->strm_list));
|
||||
+ }
|
||||
+
|
||||
+ return decomp_strm;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
+{
|
||||
+ int res;
|
||||
+ struct squashfs_stream *stream = msblk->stream;
|
||||
+ struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
|
||||
+ res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
|
||||
+ buffer, bh, b, offset, length, srclength, pages);
|
||||
+ put_decomp_stream(decomp_stream, stream);
|
||||
+ if (res < 0)
|
||||
+ ERROR("%s decompression failed, data probably corrupt\n",
|
||||
+ msblk->decompressor->name);
|
||||
+ return res;
|
||||
+}
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,231 +0,0 @@
|
||||
From d208383d640727b70cd6689bc17e67e9b5ebf4ff Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Mon, 18 Nov 2013 02:31:36 +0000
|
||||
Subject: [PATCH 3/7] Squashfs: add multi-threaded decompression using percpu
|
||||
variable
|
||||
|
||||
Add a multi-threaded decompression implementation which uses
|
||||
percpu variables.
|
||||
|
||||
Using percpu variables has advantages and disadvantages over
|
||||
implementations which do not use percpu variables.
|
||||
|
||||
Advantages:
|
||||
* the nature of percpu variables ensures decompression is
|
||||
load-balanced across the multiple cores.
|
||||
* simplicity.
|
||||
|
||||
Disadvantages: it limits decompression to one thread per core.
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
---
|
||||
fs/squashfs/Kconfig | 57 ++++++++++++++----
|
||||
fs/squashfs/Makefile | 10 +---
|
||||
fs/squashfs/decompressor_multi_percpu.c | 98 +++++++++++++++++++++++++++++++
|
||||
3 files changed, 145 insertions(+), 20 deletions(-)
|
||||
create mode 100644 fs/squashfs/decompressor_multi_percpu.c
|
||||
|
||||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
|
||||
index 1c6d340..159bd66 100644
|
||||
--- a/fs/squashfs/Kconfig
|
||||
+++ b/fs/squashfs/Kconfig
|
||||
@@ -25,6 +25,50 @@ config SQUASHFS
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
+choice
|
||||
+ prompt "Decompressor parallelisation options"
|
||||
+ depends on SQUASHFS
|
||||
+ help
|
||||
+ Squashfs now supports three parallelisation options for
|
||||
+ decompression. Each one exhibits various trade-offs between
|
||||
+ decompression performance and CPU and memory usage.
|
||||
+
|
||||
+ If in doubt, select "Single threaded compression"
|
||||
+
|
||||
+config SQUASHFS_DECOMP_SINGLE
|
||||
+ bool "Single threaded compression"
|
||||
+ help
|
||||
+ Traditionally Squashfs has used single-threaded decompression.
|
||||
+ Only one block (data or metadata) can be decompressed at any
|
||||
+ one time. This limits CPU and memory usage to a minimum.
|
||||
+
|
||||
+config SQUASHFS_DECOMP_MULTI
|
||||
+ bool "Use multiple decompressors for parallel I/O"
|
||||
+ help
|
||||
+ By default Squashfs uses a single decompressor but it gives
|
||||
+ poor performance on parallel I/O workloads when using multiple CPU
|
||||
+ machines due to waiting on decompressor availability.
|
||||
+
|
||||
+ If you have a parallel I/O workload and your system has enough memory,
|
||||
+ using this option may improve overall I/O performance.
|
||||
+
|
||||
+ This decompressor implementation uses up to two parallel
|
||||
+ decompressors per core. It dynamically allocates decompressors
|
||||
+ on a demand basis.
|
||||
+
|
||||
+config SQUASHFS_DECOMP_MULTI_PERCPU
|
||||
+ bool "Use percpu multiple decompressors for parallel I/O"
|
||||
+ help
|
||||
+ By default Squashfs uses a single decompressor but it gives
|
||||
+ poor performance on parallel I/O workloads when using multiple CPU
|
||||
+ machines due to waiting on decompressor availability.
|
||||
+
|
||||
+ This decompressor implementation uses a maximum of one
|
||||
+ decompressor per core. It uses percpu variables to ensure
|
||||
+ decompression is load-balanced across the cores.
|
||||
+
|
||||
+endchoice
|
||||
+
|
||||
config SQUASHFS_XATTR
|
||||
bool "Squashfs XATTR support"
|
||||
depends on SQUASHFS
|
||||
@@ -63,19 +107,6 @@ config SQUASHFS_LZO
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
-config SQUASHFS_MULTI_DECOMPRESSOR
|
||||
- bool "Use multiple decompressors for handling parallel I/O"
|
||||
- depends on SQUASHFS
|
||||
- help
|
||||
- By default Squashfs uses a single decompressor but it gives
|
||||
- poor performance on parallel I/O workloads when using multiple CPU
|
||||
- machines due to waiting on decompressor availability.
|
||||
-
|
||||
- If you have a parallel I/O workload and your system has enough memory,
|
||||
- using this option may improve overall I/O performance.
|
||||
-
|
||||
- If unsure, say N.
|
||||
-
|
||||
config SQUASHFS_XZ
|
||||
bool "Include support for XZ compressed file systems"
|
||||
depends on SQUASHFS
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index dfebc3b..5833b96 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -5,14 +5,10 @@
|
||||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
-
|
||||
+squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
|
||||
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
|
||||
+squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
|
||||
squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
|
||||
squashfs-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o
|
||||
-
|
||||
-ifdef CONFIG_SQUASHFS_MULTI_DECOMPRESSOR
|
||||
- squashfs-y += decompressor_multi.o
|
||||
-else
|
||||
- squashfs-y += decompressor_single.o
|
||||
-endif
|
||||
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
|
||||
new file mode 100644
|
||||
index 0000000..0e7b679
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/decompressor_multi_percpu.c
|
||||
@@ -0,0 +1,98 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/percpu.h>
|
||||
+#include <linux/buffer_head.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "decompressor.h"
|
||||
+#include "squashfs.h"
|
||||
+
|
||||
+/*
|
||||
+ * This file implements multi-threaded decompression using percpu
|
||||
+ * variables, one thread per cpu core.
|
||||
+ */
|
||||
+
|
||||
+struct squashfs_stream {
|
||||
+ void *stream;
|
||||
+};
|
||||
+
|
||||
+void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
|
||||
+ void *comp_opts)
|
||||
+{
|
||||
+ struct squashfs_stream *stream;
|
||||
+ struct squashfs_stream __percpu *percpu;
|
||||
+ int err, cpu;
|
||||
+
|
||||
+ percpu = alloc_percpu(struct squashfs_stream);
|
||||
+ if (percpu == NULL)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ stream = per_cpu_ptr(percpu, cpu);
|
||||
+ stream->stream = msblk->decompressor->init(msblk, comp_opts);
|
||||
+ if (IS_ERR(stream->stream)) {
|
||||
+ err = PTR_ERR(stream->stream);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ kfree(comp_opts);
|
||||
+ return (__force void *) percpu;
|
||||
+
|
||||
+out:
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ stream = per_cpu_ptr(percpu, cpu);
|
||||
+ if (!IS_ERR_OR_NULL(stream->stream))
|
||||
+ msblk->decompressor->free(stream->stream);
|
||||
+ }
|
||||
+ free_percpu(percpu);
|
||||
+ return ERR_PTR(err);
|
||||
+}
|
||||
+
|
||||
+void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
+{
|
||||
+ struct squashfs_stream __percpu *percpu =
|
||||
+ (struct squashfs_stream __percpu *) msblk->stream;
|
||||
+ struct squashfs_stream *stream;
|
||||
+ int cpu;
|
||||
+
|
||||
+ if (msblk->stream) {
|
||||
+ for_each_possible_cpu(cpu) {
|
||||
+ stream = per_cpu_ptr(percpu, cpu);
|
||||
+ msblk->decompressor->free(stream->stream);
|
||||
+ }
|
||||
+ free_percpu(percpu);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
+ void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
+ int srclength, int pages)
|
||||
+{
|
||||
+ struct squashfs_stream __percpu *percpu =
|
||||
+ (struct squashfs_stream __percpu *) msblk->stream;
|
||||
+ struct squashfs_stream *stream = get_cpu_ptr(percpu);
|
||||
+ int res = msblk->decompressor->decompress(msblk, stream->stream, buffer,
|
||||
+ bh, b, offset, length, srclength, pages);
|
||||
+ put_cpu_ptr(stream);
|
||||
+
|
||||
+ if (res < 0)
|
||||
+ ERROR("%s decompression failed, data probably corrupt\n",
|
||||
+ msblk->decompressor->name);
|
||||
+
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+int squashfs_max_decompressors(void)
|
||||
+{
|
||||
+ return num_possible_cpus();
|
||||
+}
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,636 +0,0 @@
|
||||
From 846b730e99518a1c9945afcb2afbe4d08a02ed80 Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Mon, 18 Nov 2013 02:59:12 +0000
|
||||
Subject: [PATCH 4/7] Squashfs: Generalise paging handling in the
|
||||
decompressors
|
||||
|
||||
Further generalise the decompressors by adding a page handler
|
||||
abstraction. This adds helpers to allow the decompressors
|
||||
to access and process the output buffers in an implementation
|
||||
independant manner.
|
||||
|
||||
This allows different types of output buffer to be passed
|
||||
to the decompressors, with the implementation specific
|
||||
aspects handled at decompression time, but without the
|
||||
knowledge being held in the decompressor wrapper code.
|
||||
|
||||
This will allow the decompressors to handle Squashfs
|
||||
cache buffers, and page cache pages.
|
||||
|
||||
This patch adds the abstraction and an implementation for
|
||||
the caches.
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Reviewed-by: Minchan Kim <minchan@kernel.org>
|
||||
---
|
||||
fs/squashfs/block.c | 27 +++++++++--------
|
||||
fs/squashfs/cache.c | 28 ++++++++++++++----
|
||||
fs/squashfs/decompressor.c | 14 +++++++--
|
||||
fs/squashfs/decompressor.h | 5 ++--
|
||||
fs/squashfs/decompressor_multi.c | 7 ++---
|
||||
fs/squashfs/decompressor_multi_percpu.c | 9 +++---
|
||||
fs/squashfs/decompressor_single.c | 9 +++---
|
||||
fs/squashfs/lzo_wrapper.c | 27 +++++++++++------
|
||||
fs/squashfs/page_actor.h | 49 +++++++++++++++++++++++++++++++
|
||||
fs/squashfs/squashfs.h | 8 ++---
|
||||
fs/squashfs/squashfs_fs_sb.h | 1 +
|
||||
fs/squashfs/xz_wrapper.c | 22 ++++++++------
|
||||
fs/squashfs/zlib_wrapper.c | 24 +++++++++------
|
||||
13 files changed, 163 insertions(+), 67 deletions(-)
|
||||
create mode 100644 fs/squashfs/page_actor.h
|
||||
|
||||
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
|
||||
index 4dd4025..0cea9b9 100644
|
||||
--- a/fs/squashfs/block.c
|
||||
+++ b/fs/squashfs/block.c
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
/*
|
||||
* Read the metadata block length, this is stored in the first two
|
||||
@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb,
|
||||
* generated a larger block - this does occasionally happen with compression
|
||||
* algorithms).
|
||||
*/
|
||||
-int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
- int length, u64 *next_index, int srclength, int pages)
|
||||
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
|
||||
+ u64 *next_index, struct squashfs_page_actor *output)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
struct buffer_head **bh;
|
||||
int offset = index & ((1 << msblk->devblksize_log2) - 1);
|
||||
u64 cur_index = index >> msblk->devblksize_log2;
|
||||
- int bytes, compressed, b = 0, k = 0, page = 0, avail, i;
|
||||
+ int bytes, compressed, b = 0, k = 0, avail, i;
|
||||
|
||||
- bh = kcalloc(((srclength + msblk->devblksize - 1)
|
||||
+ bh = kcalloc(((output->length + msblk->devblksize - 1)
|
||||
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
|
||||
if (bh == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
*next_index = index + length;
|
||||
|
||||
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
|
||||
- index, compressed ? "" : "un", length, srclength);
|
||||
+ index, compressed ? "" : "un", length, output->length);
|
||||
|
||||
- if (length < 0 || length > srclength ||
|
||||
+ if (length < 0 || length > output->length ||
|
||||
(index + length) > msblk->bytes_used)
|
||||
goto read_failure;
|
||||
|
||||
@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
|
||||
compressed ? "" : "un", length);
|
||||
|
||||
- if (length < 0 || length > srclength ||
|
||||
+ if (length < 0 || length > output->length ||
|
||||
(index + length) > msblk->bytes_used)
|
||||
goto block_release;
|
||||
|
||||
@@ -165,8 +166,8 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
}
|
||||
|
||||
if (compressed) {
|
||||
- length = squashfs_decompress(msblk, buffer, bh, b, offset,
|
||||
- length, srclength, pages);
|
||||
+ length = squashfs_decompress(msblk, bh, b, offset, length,
|
||||
+ output);
|
||||
if (length < 0)
|
||||
goto read_failure;
|
||||
} else {
|
||||
@@ -174,19 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
* Block is uncompressed.
|
||||
*/
|
||||
int in, pg_offset = 0;
|
||||
+ void *data = squashfs_first_page(output);
|
||||
|
||||
for (bytes = length; k < b; k++) {
|
||||
in = min(bytes, msblk->devblksize - offset);
|
||||
bytes -= in;
|
||||
while (in) {
|
||||
if (pg_offset == PAGE_CACHE_SIZE) {
|
||||
- page++;
|
||||
+ data = squashfs_next_page(output);
|
||||
pg_offset = 0;
|
||||
}
|
||||
avail = min_t(int, in, PAGE_CACHE_SIZE -
|
||||
pg_offset);
|
||||
- memcpy(buffer[page] + pg_offset,
|
||||
- bh[k]->b_data + offset, avail);
|
||||
+ memcpy(data + pg_offset, bh[k]->b_data + offset,
|
||||
+ avail);
|
||||
in -= avail;
|
||||
pg_offset += avail;
|
||||
offset += avail;
|
||||
@@ -194,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
|
||||
offset = 0;
|
||||
put_bh(bh[k]);
|
||||
}
|
||||
+ squashfs_finish_page(output);
|
||||
}
|
||||
|
||||
kfree(bh);
|
||||
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
|
||||
index af0b738..1cb70a0 100644
|
||||
--- a/fs/squashfs/cache.c
|
||||
+++ b/fs/squashfs/cache.c
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "squashfs_fs.h"
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
/*
|
||||
* Look-up block in cache, and increment usage count. If not in cache, read
|
||||
@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
|
||||
entry->error = 0;
|
||||
spin_unlock(&cache->lock);
|
||||
|
||||
- entry->length = squashfs_read_data(sb, entry->data,
|
||||
- block, length, &entry->next_index,
|
||||
- cache->block_size, cache->pages);
|
||||
+ entry->length = squashfs_read_data(sb, block, length,
|
||||
+ &entry->next_index, entry->actor);
|
||||
|
||||
spin_lock(&cache->lock);
|
||||
|
||||
@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
|
||||
kfree(cache->entry[i].data[j]);
|
||||
kfree(cache->entry[i].data);
|
||||
}
|
||||
+ kfree(cache->entry[i].actor);
|
||||
}
|
||||
|
||||
kfree(cache->entry);
|
||||
@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ entry->actor = squashfs_page_actor_init(entry->data,
|
||||
+ cache->pages, 0);
|
||||
+ if (entry->actor == NULL) {
|
||||
+ ERROR("Failed to allocate %s cache entry\n", name);
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
}
|
||||
|
||||
return cache;
|
||||
@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
|
||||
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||
int i, res;
|
||||
void *table, *buffer, **data;
|
||||
+ struct squashfs_page_actor *actor;
|
||||
|
||||
table = buffer = kmalloc(length, GFP_KERNEL);
|
||||
if (table == NULL)
|
||||
@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
+ actor = squashfs_page_actor_init(data, pages, length);
|
||||
+ if (actor == NULL) {
|
||||
+ res = -ENOMEM;
|
||||
+ goto failed2;
|
||||
+ }
|
||||
+
|
||||
for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
|
||||
data[i] = buffer;
|
||||
|
||||
- res = squashfs_read_data(sb, data, block, length |
|
||||
- SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages);
|
||||
+ res = squashfs_read_data(sb, block, length |
|
||||
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
|
||||
|
||||
kfree(data);
|
||||
+ kfree(actor);
|
||||
|
||||
if (res < 0)
|
||||
goto failed;
|
||||
|
||||
return table;
|
||||
|
||||
+failed2:
|
||||
+ kfree(data);
|
||||
failed:
|
||||
kfree(table);
|
||||
return ERR_PTR(res);
|
||||
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
|
||||
index 234291f..ac22fe7 100644
|
||||
--- a/fs/squashfs/decompressor.c
|
||||
+++ b/fs/squashfs/decompressor.c
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "decompressor.h"
|
||||
#include "squashfs.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
/*
|
||||
* This file (and decompressor.h) implements a decompressor framework for
|
||||
@@ -87,6 +88,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
|
||||
{
|
||||
struct squashfs_sb_info *msblk = sb->s_fs_info;
|
||||
void *buffer = NULL, *comp_opts;
|
||||
+ struct squashfs_page_actor *actor = NULL;
|
||||
int length = 0;
|
||||
|
||||
/*
|
||||
@@ -99,9 +101,14 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- length = squashfs_read_data(sb, &buffer,
|
||||
- sizeof(struct squashfs_super_block), 0, NULL,
|
||||
- PAGE_CACHE_SIZE, 1);
|
||||
+ actor = squashfs_page_actor_init(&buffer, 1, 0);
|
||||
+ if (actor == NULL) {
|
||||
+ comp_opts = ERR_PTR(-ENOMEM);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ length = squashfs_read_data(sb,
|
||||
+ sizeof(struct squashfs_super_block), 0, NULL, actor);
|
||||
|
||||
if (length < 0) {
|
||||
comp_opts = ERR_PTR(length);
|
||||
@@ -112,6 +119,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
|
||||
comp_opts = squashfs_comp_opts(msblk, buffer, length);
|
||||
|
||||
out:
|
||||
+ kfree(actor);
|
||||
kfree(buffer);
|
||||
return comp_opts;
|
||||
}
|
||||
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
|
||||
index 6cdb20a..af09853 100644
|
||||
--- a/fs/squashfs/decompressor.h
|
||||
+++ b/fs/squashfs/decompressor.h
|
||||
@@ -27,8 +27,9 @@ struct squashfs_decompressor {
|
||||
void *(*init)(struct squashfs_sb_info *, void *);
|
||||
void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
|
||||
void (*free)(void *);
|
||||
- int (*decompress)(struct squashfs_sb_info *, void *, void **,
|
||||
- struct buffer_head **, int, int, int, int, int);
|
||||
+ int (*decompress)(struct squashfs_sb_info *, void *,
|
||||
+ struct buffer_head **, int, int, int,
|
||||
+ struct squashfs_page_actor *);
|
||||
int id;
|
||||
char *name;
|
||||
int supported;
|
||||
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
|
||||
index 462731d..ae54675 100644
|
||||
--- a/fs/squashfs/decompressor_multi.c
|
||||
+++ b/fs/squashfs/decompressor_multi.c
|
||||
@@ -183,15 +183,14 @@ wait:
|
||||
}
|
||||
|
||||
|
||||
-int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
|
||||
+ int b, int offset, int length, struct squashfs_page_actor *output)
|
||||
{
|
||||
int res;
|
||||
struct squashfs_stream *stream = msblk->stream;
|
||||
struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
|
||||
res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
|
||||
- buffer, bh, b, offset, length, srclength, pages);
|
||||
+ bh, b, offset, length, output);
|
||||
put_decomp_stream(decomp_stream, stream);
|
||||
if (res < 0)
|
||||
ERROR("%s decompression failed, data probably corrupt\n",
|
||||
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
|
||||
index 0e7b679..23a9c28 100644
|
||||
--- a/fs/squashfs/decompressor_multi_percpu.c
|
||||
+++ b/fs/squashfs/decompressor_multi_percpu.c
|
||||
@@ -74,15 +74,14 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
}
|
||||
}
|
||||
|
||||
-int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
|
||||
+ int b, int offset, int length, struct squashfs_page_actor *output)
|
||||
{
|
||||
struct squashfs_stream __percpu *percpu =
|
||||
(struct squashfs_stream __percpu *) msblk->stream;
|
||||
struct squashfs_stream *stream = get_cpu_ptr(percpu);
|
||||
- int res = msblk->decompressor->decompress(msblk, stream->stream, buffer,
|
||||
- bh, b, offset, length, srclength, pages);
|
||||
+ int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
|
||||
+ offset, length, output);
|
||||
put_cpu_ptr(stream);
|
||||
|
||||
if (res < 0)
|
||||
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
|
||||
index f857cf6..a6c7592 100644
|
||||
--- a/fs/squashfs/decompressor_single.c
|
||||
+++ b/fs/squashfs/decompressor_single.c
|
||||
@@ -61,16 +61,15 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
}
|
||||
}
|
||||
|
||||
-int squashfs_decompress(struct squashfs_sb_info *msblk,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
|
||||
+ int b, int offset, int length, struct squashfs_page_actor *output)
|
||||
{
|
||||
int res;
|
||||
struct squashfs_stream *stream = msblk->stream;
|
||||
|
||||
mutex_lock(&stream->mutex);
|
||||
- res = msblk->decompressor->decompress(msblk, stream->stream, buffer,
|
||||
- bh, b, offset, length, srclength, pages);
|
||||
+ res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
|
||||
+ offset, length, output);
|
||||
mutex_unlock(&stream->mutex);
|
||||
|
||||
if (res < 0)
|
||||
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
|
||||
index 75c3b57..244b9fb 100644
|
||||
--- a/fs/squashfs/lzo_wrapper.c
|
||||
+++ b/fs/squashfs/lzo_wrapper.c
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
struct squashfs_lzo {
|
||||
void *input;
|
||||
@@ -75,13 +76,13 @@ static void lzo_free(void *strm)
|
||||
|
||||
|
||||
static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+ struct buffer_head **bh, int b, int offset, int length,
|
||||
+ struct squashfs_page_actor *output)
|
||||
{
|
||||
struct squashfs_lzo *stream = strm;
|
||||
- void *buff = stream->input;
|
||||
+ void *buff = stream->input, *data;
|
||||
int avail, i, bytes = length, res;
|
||||
- size_t out_len = srclength;
|
||||
+ size_t out_len = output->length;
|
||||
|
||||
for (i = 0; i < b; i++) {
|
||||
avail = min(bytes, msblk->devblksize - offset);
|
||||
@@ -98,12 +99,20 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
goto failed;
|
||||
|
||||
res = bytes = (int)out_len;
|
||||
- for (i = 0, buff = stream->output; bytes && i < pages; i++) {
|
||||
- avail = min_t(int, bytes, PAGE_CACHE_SIZE);
|
||||
- memcpy(buffer[i], buff, avail);
|
||||
- buff += avail;
|
||||
- bytes -= avail;
|
||||
+ data = squashfs_first_page(output);
|
||||
+ buff = stream->output;
|
||||
+ while (data) {
|
||||
+ if (bytes <= PAGE_CACHE_SIZE) {
|
||||
+ memcpy(data, buff, bytes);
|
||||
+ break;
|
||||
+ } else {
|
||||
+ memcpy(data, buff, PAGE_CACHE_SIZE);
|
||||
+ buff += PAGE_CACHE_SIZE;
|
||||
+ bytes -= PAGE_CACHE_SIZE;
|
||||
+ data = squashfs_next_page(output);
|
||||
+ }
|
||||
}
|
||||
+ squashfs_finish_page(output);
|
||||
|
||||
return res;
|
||||
|
||||
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
|
||||
new file mode 100644
|
||||
index 0000000..5b0ba5a
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/page_actor.h
|
||||
@@ -0,0 +1,49 @@
|
||||
+#ifndef PAGE_ACTOR_H
|
||||
+#define PAGE_ACTOR_H
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+struct squashfs_page_actor {
|
||||
+ void **page;
|
||||
+ int pages;
|
||||
+ int length;
|
||||
+ int next_page;
|
||||
+};
|
||||
+
|
||||
+static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
|
||||
+ int pages, int length)
|
||||
+{
|
||||
+ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
+
|
||||
+ if (actor == NULL)
|
||||
+ return NULL;
|
||||
+
|
||||
+ actor->length = length ? : pages * PAGE_CACHE_SIZE;
|
||||
+ actor->page = page;
|
||||
+ actor->pages = pages;
|
||||
+ actor->next_page = 0;
|
||||
+ return actor;
|
||||
+}
|
||||
+
|
||||
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ actor->next_page = 1;
|
||||
+ return actor->page[0];
|
||||
+}
|
||||
+
|
||||
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ return actor->next_page == actor->pages ? NULL :
|
||||
+ actor->page[actor->next_page++];
|
||||
+}
|
||||
+
|
||||
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ /* empty */
|
||||
+}
|
||||
+#endif
|
||||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
|
||||
index 2e2751d..6a97e63 100644
|
||||
--- a/fs/squashfs/squashfs.h
|
||||
+++ b/fs/squashfs/squashfs.h
|
||||
@@ -28,8 +28,8 @@
|
||||
#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args)
|
||||
|
||||
/* block.c */
|
||||
-extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
|
||||
- int, int);
|
||||
+extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
|
||||
+ struct squashfs_page_actor *);
|
||||
|
||||
/* cache.c */
|
||||
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
|
||||
@@ -53,8 +53,8 @@ extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
|
||||
/* decompressor_xxx.c */
|
||||
extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
|
||||
extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
|
||||
-extern int squashfs_decompress(struct squashfs_sb_info *, void **,
|
||||
- struct buffer_head **, int, int, int, int, int);
|
||||
+extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
|
||||
+ int, int, int, struct squashfs_page_actor *);
|
||||
extern int squashfs_max_decompressors(void);
|
||||
|
||||
/* export.c */
|
||||
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
|
||||
index 9cdcf41..1da565c 100644
|
||||
--- a/fs/squashfs/squashfs_fs_sb.h
|
||||
+++ b/fs/squashfs/squashfs_fs_sb.h
|
||||
@@ -50,6 +50,7 @@ struct squashfs_cache_entry {
|
||||
wait_queue_head_t wait_queue;
|
||||
struct squashfs_cache *cache;
|
||||
void **data;
|
||||
+ struct squashfs_page_actor *actor;
|
||||
};
|
||||
|
||||
struct squashfs_sb_info {
|
||||
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
|
||||
index 5d1d07c..c609624 100644
|
||||
--- a/fs/squashfs/xz_wrapper.c
|
||||
+++ b/fs/squashfs/xz_wrapper.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
struct squashfs_xz {
|
||||
struct xz_dec *state;
|
||||
@@ -129,11 +130,11 @@ static void squashfs_xz_free(void *strm)
|
||||
|
||||
|
||||
static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+ struct buffer_head **bh, int b, int offset, int length,
|
||||
+ struct squashfs_page_actor *output)
|
||||
{
|
||||
enum xz_ret xz_err;
|
||||
- int avail, total = 0, k = 0, page = 0;
|
||||
+ int avail, total = 0, k = 0;
|
||||
struct squashfs_xz *stream = strm;
|
||||
|
||||
xz_dec_reset(stream->state);
|
||||
@@ -141,7 +142,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
stream->buf.in_size = 0;
|
||||
stream->buf.out_pos = 0;
|
||||
stream->buf.out_size = PAGE_CACHE_SIZE;
|
||||
- stream->buf.out = buffer[page++];
|
||||
+ stream->buf.out = squashfs_first_page(output);
|
||||
|
||||
do {
|
||||
if (stream->buf.in_pos == stream->buf.in_size && k < b) {
|
||||
@@ -153,11 +154,12 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
- if (stream->buf.out_pos == stream->buf.out_size
|
||||
- && page < pages) {
|
||||
- stream->buf.out = buffer[page++];
|
||||
- stream->buf.out_pos = 0;
|
||||
- total += PAGE_CACHE_SIZE;
|
||||
+ if (stream->buf.out_pos == stream->buf.out_size) {
|
||||
+ stream->buf.out = squashfs_next_page(output);
|
||||
+ if (stream->buf.out != NULL) {
|
||||
+ stream->buf.out_pos = 0;
|
||||
+ total += PAGE_CACHE_SIZE;
|
||||
+ }
|
||||
}
|
||||
|
||||
xz_err = xz_dec_run(stream->state, &stream->buf);
|
||||
@@ -166,6 +168,8 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
put_bh(bh[k++]);
|
||||
} while (xz_err == XZ_OK);
|
||||
|
||||
+ squashfs_finish_page(output);
|
||||
+
|
||||
if (xz_err != XZ_STREAM_END || k < b)
|
||||
goto out;
|
||||
|
||||
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
|
||||
index bb04902..8727cab 100644
|
||||
--- a/fs/squashfs/zlib_wrapper.c
|
||||
+++ b/fs/squashfs/zlib_wrapper.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "squashfs_fs_sb.h"
|
||||
#include "squashfs.h"
|
||||
#include "decompressor.h"
|
||||
+#include "page_actor.h"
|
||||
|
||||
static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
|
||||
{
|
||||
@@ -62,14 +63,14 @@ static void zlib_free(void *strm)
|
||||
|
||||
|
||||
static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
- void **buffer, struct buffer_head **bh, int b, int offset, int length,
|
||||
- int srclength, int pages)
|
||||
+ struct buffer_head **bh, int b, int offset, int length,
|
||||
+ struct squashfs_page_actor *output)
|
||||
{
|
||||
- int zlib_err, zlib_init = 0;
|
||||
- int k = 0, page = 0;
|
||||
+ int zlib_err, zlib_init = 0, k = 0;
|
||||
z_stream *stream = strm;
|
||||
|
||||
- stream->avail_out = 0;
|
||||
+ stream->avail_out = PAGE_CACHE_SIZE;
|
||||
+ stream->next_out = squashfs_first_page(output);
|
||||
stream->avail_in = 0;
|
||||
|
||||
do {
|
||||
@@ -81,15 +82,18 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
- if (stream->avail_out == 0 && page < pages) {
|
||||
- stream->next_out = buffer[page++];
|
||||
- stream->avail_out = PAGE_CACHE_SIZE;
|
||||
+ if (stream->avail_out == 0) {
|
||||
+ stream->next_out = squashfs_next_page(output);
|
||||
+ if (stream->next_out != NULL)
|
||||
+ stream->avail_out = PAGE_CACHE_SIZE;
|
||||
}
|
||||
|
||||
if (!zlib_init) {
|
||||
zlib_err = zlib_inflateInit(stream);
|
||||
- if (zlib_err != Z_OK)
|
||||
+ if (zlib_err != Z_OK) {
|
||||
+ squashfs_finish_page(output);
|
||||
goto out;
|
||||
+ }
|
||||
zlib_init = 1;
|
||||
}
|
||||
|
||||
@@ -99,6 +103,8 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
|
||||
put_bh(bh[k++]);
|
||||
} while (zlib_err == Z_OK);
|
||||
|
||||
+ squashfs_finish_page(output);
|
||||
+
|
||||
if (zlib_err != Z_STREAM_END)
|
||||
goto out;
|
||||
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,275 +0,0 @@
|
||||
From 5f55dbc0c5c466a9cdfa4da7ac1bfe351c7fc52a Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Thu, 31 Oct 2013 19:24:27 +0000
|
||||
Subject: [PATCH 5/7] Squashfs: Restructure squashfs_readpage()
|
||||
|
||||
Restructure squashfs_readpage() splitting it into separate
|
||||
functions for datablocks, fragments and sparse blocks.
|
||||
|
||||
Move the memcpying (from squashfs cache entry) implementation of
|
||||
squashfs_readpage_block into file_cache.c
|
||||
|
||||
This allows different implementations to be supported.
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Reviewed-by: Minchan Kim <minchan@kernel.org>
|
||||
---
|
||||
fs/squashfs/Makefile | 2 +-
|
||||
fs/squashfs/file.c | 142 +++++++++++++++++++++++-----------------------
|
||||
fs/squashfs/file_cache.c | 38 +++++++++++++
|
||||
fs/squashfs/squashfs.h | 7 +++
|
||||
4 files changed, 118 insertions(+), 71 deletions(-)
|
||||
create mode 100644 fs/squashfs/file_cache.c
|
||||
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index 5833b96..e01ba11 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
-squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
+squashfs-y += namei.o super.o symlink.o decompressor.o file_cache.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
|
||||
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
|
||||
index 8ca62c2..e5c9689 100644
|
||||
--- a/fs/squashfs/file.c
|
||||
+++ b/fs/squashfs/file.c
|
||||
@@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
|
||||
return le32_to_cpu(size);
|
||||
}
|
||||
|
||||
-
|
||||
-static int squashfs_readpage(struct file *file, struct page *page)
|
||||
+/* Copy data into page cache */
|
||||
+void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
|
||||
+ int bytes, int offset)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
- int bytes, i, offset = 0, sparse = 0;
|
||||
- struct squashfs_cache_entry *buffer = NULL;
|
||||
void *pageaddr;
|
||||
-
|
||||
- int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
|
||||
- int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
|
||||
- int start_index = page->index & ~mask;
|
||||
- int end_index = start_index | mask;
|
||||
- int file_end = i_size_read(inode) >> msblk->block_log;
|
||||
-
|
||||
- TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
|
||||
- page->index, squashfs_i(inode)->start);
|
||||
-
|
||||
- if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
|
||||
- PAGE_CACHE_SHIFT))
|
||||
- goto out;
|
||||
-
|
||||
- if (index < file_end || squashfs_i(inode)->fragment_block ==
|
||||
- SQUASHFS_INVALID_BLK) {
|
||||
- /*
|
||||
- * Reading a datablock from disk. Need to read block list
|
||||
- * to get location and block size.
|
||||
- */
|
||||
- u64 block = 0;
|
||||
- int bsize = read_blocklist(inode, index, &block);
|
||||
- if (bsize < 0)
|
||||
- goto error_out;
|
||||
-
|
||||
- if (bsize == 0) { /* hole */
|
||||
- bytes = index == file_end ?
|
||||
- (i_size_read(inode) & (msblk->block_size - 1)) :
|
||||
- msblk->block_size;
|
||||
- sparse = 1;
|
||||
- } else {
|
||||
- /*
|
||||
- * Read and decompress datablock.
|
||||
- */
|
||||
- buffer = squashfs_get_datablock(inode->i_sb,
|
||||
- block, bsize);
|
||||
- if (buffer->error) {
|
||||
- ERROR("Unable to read page, block %llx, size %x"
|
||||
- "\n", block, bsize);
|
||||
- squashfs_cache_put(buffer);
|
||||
- goto error_out;
|
||||
- }
|
||||
- bytes = buffer->length;
|
||||
- }
|
||||
- } else {
|
||||
- /*
|
||||
- * Datablock is stored inside a fragment (tail-end packed
|
||||
- * block).
|
||||
- */
|
||||
- buffer = squashfs_get_fragment(inode->i_sb,
|
||||
- squashfs_i(inode)->fragment_block,
|
||||
- squashfs_i(inode)->fragment_size);
|
||||
-
|
||||
- if (buffer->error) {
|
||||
- ERROR("Unable to read page, block %llx, size %x\n",
|
||||
- squashfs_i(inode)->fragment_block,
|
||||
- squashfs_i(inode)->fragment_size);
|
||||
- squashfs_cache_put(buffer);
|
||||
- goto error_out;
|
||||
- }
|
||||
- bytes = i_size_read(inode) & (msblk->block_size - 1);
|
||||
- offset = squashfs_i(inode)->fragment_offset;
|
||||
- }
|
||||
+ int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
|
||||
+ int start_index = page->index & ~mask, end_index = start_index | mask;
|
||||
|
||||
/*
|
||||
* Loop copying datablock into pages. As the datablock likely covers
|
||||
@@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page)
|
||||
for (i = start_index; i <= end_index && bytes > 0; i++,
|
||||
bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
|
||||
struct page *push_page;
|
||||
- int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
|
||||
+ int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
|
||||
|
||||
TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
|
||||
|
||||
@@ -475,11 +413,75 @@ skip_page:
|
||||
if (i != page->index)
|
||||
page_cache_release(push_page);
|
||||
}
|
||||
+}
|
||||
+
|
||||
+/* Read datablock stored packed inside a fragment (tail-end packed block) */
|
||||
+static int squashfs_readpage_fragment(struct page *page)
|
||||
+{
|
||||
+ struct inode *inode = page->mapping->host;
|
||||
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
+ struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
|
||||
+ squashfs_i(inode)->fragment_block,
|
||||
+ squashfs_i(inode)->fragment_size);
|
||||
+ int res = buffer->error;
|
||||
+
|
||||
+ if (res)
|
||||
+ ERROR("Unable to read page, block %llx, size %x\n",
|
||||
+ squashfs_i(inode)->fragment_block,
|
||||
+ squashfs_i(inode)->fragment_size);
|
||||
+ else
|
||||
+ squashfs_copy_cache(page, buffer, i_size_read(inode) &
|
||||
+ (msblk->block_size - 1),
|
||||
+ squashfs_i(inode)->fragment_offset);
|
||||
+
|
||||
+ squashfs_cache_put(buffer);
|
||||
+ return res;
|
||||
+}
|
||||
|
||||
- if (!sparse)
|
||||
- squashfs_cache_put(buffer);
|
||||
+static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
|
||||
+{
|
||||
+ struct inode *inode = page->mapping->host;
|
||||
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
+ int bytes = index == file_end ?
|
||||
+ (i_size_read(inode) & (msblk->block_size - 1)) :
|
||||
+ msblk->block_size;
|
||||
|
||||
+ squashfs_copy_cache(page, NULL, bytes, 0);
|
||||
return 0;
|
||||
+}
|
||||
+
|
||||
+static int squashfs_readpage(struct file *file, struct page *page)
|
||||
+{
|
||||
+ struct inode *inode = page->mapping->host;
|
||||
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
+ int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
|
||||
+ int file_end = i_size_read(inode) >> msblk->block_log;
|
||||
+ int res;
|
||||
+ void *pageaddr;
|
||||
+
|
||||
+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
|
||||
+ page->index, squashfs_i(inode)->start);
|
||||
+
|
||||
+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
|
||||
+ PAGE_CACHE_SHIFT))
|
||||
+ goto out;
|
||||
+
|
||||
+ if (index < file_end || squashfs_i(inode)->fragment_block ==
|
||||
+ SQUASHFS_INVALID_BLK) {
|
||||
+ u64 block = 0;
|
||||
+ int bsize = read_blocklist(inode, index, &block);
|
||||
+ if (bsize < 0)
|
||||
+ goto error_out;
|
||||
+
|
||||
+ if (bsize == 0)
|
||||
+ res = squashfs_readpage_sparse(page, index, file_end);
|
||||
+ else
|
||||
+ res = squashfs_readpage_block(page, block, bsize);
|
||||
+ } else
|
||||
+ res = squashfs_readpage_fragment(page);
|
||||
+
|
||||
+ if (!res)
|
||||
+ return 0;
|
||||
|
||||
error_out:
|
||||
SetPageError(page);
|
||||
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
|
||||
new file mode 100644
|
||||
index 0000000..f2310d2
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/file_cache.c
|
||||
@@ -0,0 +1,38 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/vfs.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include <linux/mutex.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "squashfs_fs_i.h"
|
||||
+#include "squashfs.h"
|
||||
+
|
||||
+/* Read separately compressed datablock and memcopy into page cache */
|
||||
+int squashfs_readpage_block(struct page *page, u64 block, int bsize)
|
||||
+{
|
||||
+ struct inode *i = page->mapping->host;
|
||||
+ struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
|
||||
+ block, bsize);
|
||||
+ int res = buffer->error;
|
||||
+
|
||||
+ if (res)
|
||||
+ ERROR("Unable to read page, block %llx, size %x\n", block,
|
||||
+ bsize);
|
||||
+ else
|
||||
+ squashfs_copy_cache(page, buffer, buffer->length, 0);
|
||||
+
|
||||
+ squashfs_cache_put(buffer);
|
||||
+ return res;
|
||||
+}
|
||||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
|
||||
index 6a97e63..9e1bb79 100644
|
||||
--- a/fs/squashfs/squashfs.h
|
||||
+++ b/fs/squashfs/squashfs.h
|
||||
@@ -66,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
|
||||
extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
|
||||
u64, u64, unsigned int);
|
||||
|
||||
+/* file.c */
|
||||
+void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
|
||||
+ int);
|
||||
+
|
||||
+/* file_xxx.c */
|
||||
+extern int squashfs_readpage_block(struct page *, u64, int);
|
||||
+
|
||||
/* id.c */
|
||||
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
|
||||
extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,442 +0,0 @@
|
||||
From 0d455c12c6428647547bacccaaced3cae0f35570 Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Wed, 13 Nov 2013 02:04:19 +0000
|
||||
Subject: [PATCH 6/7] Squashfs: Directly decompress into the page cache for
|
||||
file data
|
||||
|
||||
This introduces an implementation of squashfs_readpage_block()
|
||||
that directly decompresses into the page cache.
|
||||
|
||||
This uses the previously added page handler abstraction to push
|
||||
down the necessary kmap_atomic/kunmap_atomic operations on the
|
||||
page cache buffers into the decompressors. This enables
|
||||
direct copying into the page cache without using the slow
|
||||
kmap/kunmap calls.
|
||||
|
||||
The code detects when multiple threads are racing in
|
||||
squashfs_readpage() to decompress the same block, and avoids
|
||||
this regression by falling back to using an intermediate
|
||||
buffer.
|
||||
|
||||
This patch enhances the performance of Squashfs significantly
|
||||
when multiple processes are accessing the filesystem simultaneously
|
||||
because it not only reduces memcopying, but it more importantly
|
||||
eliminates the lock contention on the intermediate buffer.
|
||||
|
||||
Using single-thread decompression.
|
||||
|
||||
dd if=file1 of=/dev/null bs=4096 &
|
||||
dd if=file2 of=/dev/null bs=4096 &
|
||||
dd if=file3 of=/dev/null bs=4096 &
|
||||
dd if=file4 of=/dev/null bs=4096
|
||||
|
||||
Before:
|
||||
|
||||
629145600 bytes (629 MB) copied, 45.8046 s, 13.7 MB/s
|
||||
|
||||
After:
|
||||
|
||||
629145600 bytes (629 MB) copied, 9.29414 s, 67.7 MB/s
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Reviewed-by: Minchan Kim <minchan@kernel.org>
|
||||
---
|
||||
fs/squashfs/Kconfig | 28 ++++++++
|
||||
fs/squashfs/Makefile | 4 +-
|
||||
fs/squashfs/file_direct.c | 173 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
fs/squashfs/page_actor.c | 100 ++++++++++++++++++++++++++
|
||||
fs/squashfs/page_actor.h | 32 +++++++++
|
||||
5 files changed, 336 insertions(+), 1 deletion(-)
|
||||
create mode 100644 fs/squashfs/file_direct.c
|
||||
create mode 100644 fs/squashfs/page_actor.c
|
||||
|
||||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
|
||||
index 159bd66..b6fa865 100644
|
||||
--- a/fs/squashfs/Kconfig
|
||||
+++ b/fs/squashfs/Kconfig
|
||||
@@ -26,6 +26,34 @@ config SQUASHFS
|
||||
If unsure, say N.
|
||||
|
||||
choice
|
||||
+ prompt "File decompression options"
|
||||
+ depends on SQUASHFS
|
||||
+ help
|
||||
+ Squashfs now supports two options for decompressing file
|
||||
+ data. Traditionally Squashfs has decompressed into an
|
||||
+ intermediate buffer and then memcopied it into the page cache.
|
||||
+ Squashfs now supports the ability to decompress directly into
|
||||
+ the page cache.
|
||||
+
|
||||
+ If unsure, select "Decompress file data into an intermediate buffer"
|
||||
+
|
||||
+config SQUASHFS_FILE_CACHE
|
||||
+ bool "Decompress file data into an intermediate buffer"
|
||||
+ help
|
||||
+ Decompress file data into an intermediate buffer and then
|
||||
+ memcopy it into the page cache.
|
||||
+
|
||||
+config SQUASHFS_FILE_DIRECT
|
||||
+ bool "Decompress files directly into the page cache"
|
||||
+ help
|
||||
+ Directly decompress file data into the page cache.
|
||||
+ Doing so can significantly improve performance because
|
||||
+ it eliminates a memcpy and it also removes the lock contention
|
||||
+ on the single buffer.
|
||||
+
|
||||
+endchoice
|
||||
+
|
||||
+choice
|
||||
prompt "Decompressor parallelisation options"
|
||||
depends on SQUASHFS
|
||||
help
|
||||
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
|
||||
index e01ba11..4132520 100644
|
||||
--- a/fs/squashfs/Makefile
|
||||
+++ b/fs/squashfs/Makefile
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
|
||||
-squashfs-y += namei.o super.o symlink.o decompressor.o file_cache.o
|
||||
+squashfs-y += namei.o super.o symlink.o decompressor.o
|
||||
+squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
|
||||
+squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
|
||||
squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
|
||||
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
|
||||
new file mode 100644
|
||||
index 0000000..2943b2b
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/file_direct.c
|
||||
@@ -0,0 +1,173 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/vfs.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include <linux/mutex.h>
|
||||
+
|
||||
+#include "squashfs_fs.h"
|
||||
+#include "squashfs_fs_sb.h"
|
||||
+#include "squashfs_fs_i.h"
|
||||
+#include "squashfs.h"
|
||||
+#include "page_actor.h"
|
||||
+
|
||||
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
|
||||
+ int pages, struct page **page);
|
||||
+
|
||||
+/* Read separately compressed datablock directly into page cache */
|
||||
+int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
|
||||
+
|
||||
+{
|
||||
+ struct inode *inode = target_page->mapping->host;
|
||||
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
|
||||
+
|
||||
+ int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT;
|
||||
+ int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
|
||||
+ int start_index = target_page->index & ~mask;
|
||||
+ int end_index = start_index | mask;
|
||||
+ int i, n, pages, missing_pages, bytes, res = -ENOMEM;
|
||||
+ struct page **page;
|
||||
+ struct squashfs_page_actor *actor;
|
||||
+ void *pageaddr;
|
||||
+
|
||||
+ if (end_index > file_end)
|
||||
+ end_index = file_end;
|
||||
+
|
||||
+ pages = end_index - start_index + 1;
|
||||
+
|
||||
+ page = kmalloc(sizeof(void *) * pages, GFP_KERNEL);
|
||||
+ if (page == NULL)
|
||||
+ return res;
|
||||
+
|
||||
+ /*
|
||||
+ * Create a "page actor" which will kmap and kunmap the
|
||||
+ * page cache pages appropriately within the decompressor
|
||||
+ */
|
||||
+ actor = squashfs_page_actor_init_special(page, pages, 0);
|
||||
+ if (actor == NULL)
|
||||
+ goto out;
|
||||
+
|
||||
+ /* Try to grab all the pages covered by the Squashfs block */
|
||||
+ for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
|
||||
+ page[i] = (n == target_page->index) ? target_page :
|
||||
+ grab_cache_page_nowait(target_page->mapping, n);
|
||||
+
|
||||
+ if (page[i] == NULL) {
|
||||
+ missing_pages++;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (PageUptodate(page[i])) {
|
||||
+ unlock_page(page[i]);
|
||||
+ page_cache_release(page[i]);
|
||||
+ page[i] = NULL;
|
||||
+ missing_pages++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (missing_pages) {
|
||||
+ /*
|
||||
+ * Couldn't get one or more pages, this page has either
|
||||
+ * been VM reclaimed, but others are still in the page cache
|
||||
+ * and uptodate, or we're racing with another thread in
|
||||
+ * squashfs_readpage also trying to grab them. Fall back to
|
||||
+ * using an intermediate buffer.
|
||||
+ */
|
||||
+ res = squashfs_read_cache(target_page, block, bsize, pages,
|
||||
+ page);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* Decompress directly into the page cache buffers */
|
||||
+ res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
|
||||
+ if (res < 0)
|
||||
+ goto mark_errored;
|
||||
+
|
||||
+ /* Last page may have trailing bytes not filled */
|
||||
+ bytes = res % PAGE_CACHE_SIZE;
|
||||
+ if (bytes) {
|
||||
+ pageaddr = kmap_atomic(page[pages - 1]);
|
||||
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
|
||||
+ kunmap_atomic(pageaddr);
|
||||
+ }
|
||||
+
|
||||
+ /* Mark pages as uptodate, unlock and release */
|
||||
+ for (i = 0; i < pages; i++) {
|
||||
+ flush_dcache_page(page[i]);
|
||||
+ SetPageUptodate(page[i]);
|
||||
+ unlock_page(page[i]);
|
||||
+ if (page[i] != target_page)
|
||||
+ page_cache_release(page[i]);
|
||||
+ }
|
||||
+
|
||||
+ kfree(actor);
|
||||
+ kfree(page);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+mark_errored:
|
||||
+ /* Decompression failed, mark pages as errored. Target_page is
|
||||
+ * dealt with by the caller
|
||||
+ */
|
||||
+ for (i = 0; i < pages; i++) {
|
||||
+ if (page[i] == target_page)
|
||||
+ continue;
|
||||
+ flush_dcache_page(page[i]);
|
||||
+ SetPageError(page[i]);
|
||||
+ unlock_page(page[i]);
|
||||
+ page_cache_release(page[i]);
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ kfree(actor);
|
||||
+ kfree(page);
|
||||
+ return res;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
|
||||
+ int pages, struct page **page)
|
||||
+{
|
||||
+ struct inode *i = target_page->mapping->host;
|
||||
+ struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
|
||||
+ block, bsize);
|
||||
+ int bytes = buffer->length, res = buffer->error, n, offset = 0;
|
||||
+ void *pageaddr;
|
||||
+
|
||||
+ if (res) {
|
||||
+ ERROR("Unable to read page, block %llx, size %x\n", block,
|
||||
+ bsize);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ for (n = 0; n < pages && bytes > 0; n++,
|
||||
+ bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
|
||||
+ int avail = min_t(int, bytes, PAGE_CACHE_SIZE);
|
||||
+
|
||||
+ if (page[n] == NULL)
|
||||
+ continue;
|
||||
+
|
||||
+ pageaddr = kmap_atomic(page[n]);
|
||||
+ squashfs_copy_data(pageaddr, buffer, offset, avail);
|
||||
+ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
|
||||
+ kunmap_atomic(pageaddr);
|
||||
+ flush_dcache_page(page[n]);
|
||||
+ SetPageUptodate(page[n]);
|
||||
+ unlock_page(page[n]);
|
||||
+ if (page[n] != target_page)
|
||||
+ page_cache_release(page[n]);
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ squashfs_cache_put(buffer);
|
||||
+ return res;
|
||||
+}
|
||||
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
|
||||
new file mode 100644
|
||||
index 0000000..5a1c11f
|
||||
--- /dev/null
|
||||
+++ b/fs/squashfs/page_actor.c
|
||||
@@ -0,0 +1,100 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2013
|
||||
+ * Phillip Lougher <phillip@squashfs.org.uk>
|
||||
+ *
|
||||
+ * This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
+ * the COPYING file in the top-level directory.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/pagemap.h>
|
||||
+#include "page_actor.h"
|
||||
+
|
||||
+/*
|
||||
+ * This file contains implementations of page_actor for decompressing into
|
||||
+ * an intermediate buffer, and for decompressing directly into the
|
||||
+ * page cache.
|
||||
+ *
|
||||
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
|
||||
+ * and squashfs_finish_page().
|
||||
+ */
|
||||
+
|
||||
+/* Implementation of page_actor for decompressing into intermediate buffer */
|
||||
+static void *cache_first_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ actor->next_page = 1;
|
||||
+ return actor->buffer[0];
|
||||
+}
|
||||
+
|
||||
+static void *cache_next_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ if (actor->next_page == actor->pages)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return actor->buffer[actor->next_page++];
|
||||
+}
|
||||
+
|
||||
+static void cache_finish_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ /* empty */
|
||||
+}
|
||||
+
|
||||
+struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
|
||||
+ int pages, int length)
|
||||
+{
|
||||
+ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
+
|
||||
+ if (actor == NULL)
|
||||
+ return NULL;
|
||||
+
|
||||
+ actor->length = length ? : pages * PAGE_CACHE_SIZE;
|
||||
+ actor->buffer = buffer;
|
||||
+ actor->pages = pages;
|
||||
+ actor->next_page = 0;
|
||||
+ actor->squashfs_first_page = cache_first_page;
|
||||
+ actor->squashfs_next_page = cache_next_page;
|
||||
+ actor->squashfs_finish_page = cache_finish_page;
|
||||
+ return actor;
|
||||
+}
|
||||
+
|
||||
+/* Implementation of page_actor for decompressing directly into page cache. */
|
||||
+static void *direct_first_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ actor->next_page = 1;
|
||||
+ return actor->pageaddr = kmap_atomic(actor->page[0]);
|
||||
+}
|
||||
+
|
||||
+static void *direct_next_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ if (actor->pageaddr)
|
||||
+ kunmap_atomic(actor->pageaddr);
|
||||
+
|
||||
+ return actor->pageaddr = actor->next_page == actor->pages ? NULL :
|
||||
+ kmap_atomic(actor->page[actor->next_page++]);
|
||||
+}
|
||||
+
|
||||
+static void direct_finish_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ if (actor->pageaddr)
|
||||
+ kunmap_atomic(actor->pageaddr);
|
||||
+}
|
||||
+
|
||||
+struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
|
||||
+ int pages, int length)
|
||||
+{
|
||||
+ struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
|
||||
+
|
||||
+ if (actor == NULL)
|
||||
+ return NULL;
|
||||
+
|
||||
+ actor->length = length ? : pages * PAGE_CACHE_SIZE;
|
||||
+ actor->page = page;
|
||||
+ actor->pages = pages;
|
||||
+ actor->next_page = 0;
|
||||
+ actor->pageaddr = NULL;
|
||||
+ actor->squashfs_first_page = direct_first_page;
|
||||
+ actor->squashfs_next_page = direct_next_page;
|
||||
+ actor->squashfs_finish_page = direct_finish_page;
|
||||
+ return actor;
|
||||
+}
|
||||
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
|
||||
index 5b0ba5a..26dd820 100644
|
||||
--- a/fs/squashfs/page_actor.h
|
||||
+++ b/fs/squashfs/page_actor.h
|
||||
@@ -8,6 +8,7 @@
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
+#ifndef CONFIG_SQUASHFS_FILE_DIRECT
|
||||
struct squashfs_page_actor {
|
||||
void **page;
|
||||
int pages;
|
||||
@@ -46,4 +47,35 @@ static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
+#else
|
||||
+struct squashfs_page_actor {
|
||||
+ union {
|
||||
+ void **buffer;
|
||||
+ struct page **page;
|
||||
+ };
|
||||
+ void *pageaddr;
|
||||
+ void *(*squashfs_first_page)(struct squashfs_page_actor *);
|
||||
+ void *(*squashfs_next_page)(struct squashfs_page_actor *);
|
||||
+ void (*squashfs_finish_page)(struct squashfs_page_actor *);
|
||||
+ int pages;
|
||||
+ int length;
|
||||
+ int next_page;
|
||||
+};
|
||||
+
|
||||
+extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
|
||||
+extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
|
||||
+ **, int, int);
|
||||
+static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ return actor->squashfs_first_page(actor);
|
||||
+}
|
||||
+static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ return actor->squashfs_next_page(actor);
|
||||
+}
|
||||
+static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
|
||||
+{
|
||||
+ actor->squashfs_finish_page(actor);
|
||||
+}
|
||||
+#endif
|
||||
#endif
|
||||
--
|
||||
1.7.10.4
|
||||
|
@ -1,38 +0,0 @@
|
||||
From ed4f381ec15e5f11724cdbc68cffd2c22d1eaebd Mon Sep 17 00:00:00 2001
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Sun, 10 Nov 2013 00:02:29 +0000
|
||||
Subject: [PATCH 7/7] Squashfs: Check stream is not NULL in
|
||||
decompressor_multi.c
|
||||
|
||||
Fix static checker complaint that stream is not checked in
|
||||
squashfs_decompressor_destroy().
|
||||
|
||||
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Reviewed-by: Minchan Kim <minchan@kernel.org>
|
||||
---
|
||||
fs/squashfs/decompressor_multi.c | 7 +++----
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
|
||||
index ae54675..d6008a6 100644
|
||||
--- a/fs/squashfs/decompressor_multi.c
|
||||
+++ b/fs/squashfs/decompressor_multi.c
|
||||
@@ -119,11 +119,10 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
|
||||
kfree(decomp_strm);
|
||||
stream->avail_decomp--;
|
||||
}
|
||||
+ WARN_ON(stream->avail_decomp);
|
||||
+ kfree(stream->comp_opts);
|
||||
+ kfree(stream);
|
||||
}
|
||||
-
|
||||
- WARN_ON(stream->avail_decomp);
|
||||
- kfree(stream->comp_opts);
|
||||
- kfree(stream);
|
||||
}
|
||||
|
||||
|
||||
--
|
||||
1.7.10.4
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
||||
From edab54fcfb1eb4b56868d2e364cf49dc3c6bad25 Mon Sep 17 00:00:00 2001
|
||||
From: Stefan Saraev <stefan@saraev.ca>
|
||||
Date: Wed, 18 Sep 2013 14:52:05 +0300
|
||||
Subject: [PATCH] A split for ds3000/ts2020 code forgot to change the TEVII_S471 code.
|
||||
Change the TEVII_S471 according the changes to TEVII_S470.
|
||||
|
||||
BP: https://linuxtv.org/patch/19731/
|
||||
---
|
||||
drivers/media/pci/cx23885/cx23885-dvb.c | 5 +++++
|
||||
1 files changed, 5 insertions(+), 0 deletions(-)
|
||||
|
||||
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
|
||||
index 5db14f8..4cbf20a 100644
|
||||
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
|
||||
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
|
||||
@@ -1339,6 +1339,11 @@ static int dvb_register(struct cx23885_tsport *port)
|
||||
fe0->dvb.frontend = dvb_attach(ds3000_attach,
|
||||
&tevii_ds3000_config,
|
||||
&i2c_bus->i2c_adap);
|
||||
+ if (fe0->dvb.frontend != NULL) {
|
||||
+ dvb_attach(ts2020_attach, fe0->dvb.frontend,
|
||||
+ &tevii_ts2020_config, &i2c_bus->i2c_adap);
|
||||
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
|
||||
+ }
|
||||
break;
|
||||
case CX23885_BOARD_BST_PS8512:
|
||||
case CX23885_BOARD_DVBSKY_S950:
|
||||
--
|
||||
1.7.2.5
|
||||
|
Loading…
x
Reference in New Issue
Block a user