Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
d5e6e0fa
Commit
d5e6e0fa
authored
Feb 06, 2017
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'synaptics-rmi4' into next
Bring in latest RMI4 support in preparation to the merge window.
parents
57b8443d
f32361b7
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
268 additions
and
57 deletions
+268
-57
drivers/input/rmi4/Kconfig
drivers/input/rmi4/Kconfig
+12
-16
drivers/input/rmi4/rmi_2d_sensor.c
drivers/input/rmi4/rmi_2d_sensor.c
+6
-1
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_bus.c
+2
-2
drivers/input/rmi4/rmi_driver.c
drivers/input/rmi4/rmi_driver.c
+16
-3
drivers/input/rmi4/rmi_driver.h
drivers/input/rmi4/rmi_driver.h
+2
-1
drivers/input/rmi4/rmi_f01.c
drivers/input/rmi4/rmi_f01.c
+102
-2
drivers/input/rmi4/rmi_f03.c
drivers/input/rmi4/rmi_f03.c
+0
-3
drivers/input/rmi4/rmi_f34.c
drivers/input/rmi4/rmi_f34.c
+113
-29
drivers/input/rmi4/rmi_f34.h
drivers/input/rmi4/rmi_f34.h
+4
-0
drivers/input/rmi4/rmi_f34v7.c
drivers/input/rmi4/rmi_f34v7.c
+11
-0
No files found.
drivers/input/rmi4/Kconfig
View file @
d5e6e0fa
...
@@ -9,9 +9,11 @@ config RMI4_CORE
...
@@ -9,9 +9,11 @@ config RMI4_CORE
If unsure, say Y.
If unsure, say Y.
if RMI4_CORE
config RMI4_I2C
config RMI4_I2C
tristate "RMI4 I2C Support"
tristate "RMI4 I2C Support"
depends on
RMI4_CORE &&
I2C
depends on I2C
help
help
Say Y here if you want to support RMI4 devices connected to an I2C
Say Y here if you want to support RMI4 devices connected to an I2C
bus.
bus.
...
@@ -20,7 +22,7 @@ config RMI4_I2C
...
@@ -20,7 +22,7 @@ config RMI4_I2C
config RMI4_SPI
config RMI4_SPI
tristate "RMI4 SPI Support"
tristate "RMI4 SPI Support"
depends on
RMI4_CORE &&
SPI
depends on SPI
help
help
Say Y here if you want to support RMI4 devices connected to a SPI
Say Y here if you want to support RMI4 devices connected to a SPI
bus.
bus.
...
@@ -29,7 +31,7 @@ config RMI4_SPI
...
@@ -29,7 +31,7 @@ config RMI4_SPI
config RMI4_SMB
config RMI4_SMB
tristate "RMI4 SMB Support"
tristate "RMI4 SMB Support"
depends on
RMI4_CORE &&
I2C
depends on I2C
help
help
Say Y here if you want to support RMI4 devices connected to an SMB
Say Y here if you want to support RMI4 devices connected to an SMB
bus.
bus.
...
@@ -40,23 +42,20 @@ config RMI4_SMB
...
@@ -40,23 +42,20 @@ config RMI4_SMB
called rmi_smbus.
called rmi_smbus.
config RMI4_F03
config RMI4_F03
bool "RMI4 Function 03 (PS2 Guest)"
bool "RMI4 Function 03 (PS2 Guest)"
depends on RMI4_CORE
depends on SERIO=y || RMI4_CORE=SERIO
depends on SERIO=y || RMI4_CORE=SERIO
help
help
Say Y here if you want to add support for RMI4 function 03.
Say Y here if you want to add support for RMI4 function 03.
Function 03 provides PS2 guest support for RMI4 devices. This
Function 03 provides PS2 guest support for RMI4 devices. This
includes support for TrackPoints on TouchPads.
includes support for TrackPoints on TouchPads.
config RMI4_2D_SENSOR
config RMI4_2D_SENSOR
bool
bool
depends on RMI4_CORE
config RMI4_F11
config RMI4_F11
bool "RMI4 Function 11 (2D pointing)"
bool "RMI4 Function 11 (2D pointing)"
select RMI4_2D_SENSOR
select RMI4_2D_SENSOR
depends on RMI4_CORE
help
help
Say Y here if you want to add support for RMI4 function 11.
Say Y here if you want to add support for RMI4 function 11.
...
@@ -67,7 +66,6 @@ config RMI4_F11
...
@@ -67,7 +66,6 @@ config RMI4_F11
config RMI4_F12
config RMI4_F12
bool "RMI4 Function 12 (2D pointing)"
bool "RMI4 Function 12 (2D pointing)"
select RMI4_2D_SENSOR
select RMI4_2D_SENSOR
depends on RMI4_CORE
help
help
Say Y here if you want to add support for RMI4 function 12.
Say Y here if you want to add support for RMI4 function 12.
...
@@ -77,7 +75,6 @@ config RMI4_F12
...
@@ -77,7 +75,6 @@ config RMI4_F12
config RMI4_F30
config RMI4_F30
bool "RMI4 Function 30 (GPIO LED)"
bool "RMI4 Function 30 (GPIO LED)"
depends on RMI4_CORE
help
help
Say Y here if you want to add support for RMI4 function 30.
Say Y here if you want to add support for RMI4 function 30.
...
@@ -86,7 +83,6 @@ config RMI4_F30
...
@@ -86,7 +83,6 @@ config RMI4_F30
config RMI4_F34
config RMI4_F34
bool "RMI4 Function 34 (Device reflash)"
bool "RMI4 Function 34 (Device reflash)"
depends on RMI4_CORE
select FW_LOADER
select FW_LOADER
help
help
Say Y here if you want to add support for RMI4 function 34.
Say Y here if you want to add support for RMI4 function 34.
...
@@ -97,7 +93,6 @@ config RMI4_F34
...
@@ -97,7 +93,6 @@ config RMI4_F34
config RMI4_F54
config RMI4_F54
bool "RMI4 Function 54 (Analog diagnostics)"
bool "RMI4 Function 54 (Analog diagnostics)"
depends on RMI4_CORE
depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m)
depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m)
select VIDEOBUF2_VMALLOC
select VIDEOBUF2_VMALLOC
select RMI4_F55
select RMI4_F55
...
@@ -109,9 +104,10 @@ config RMI4_F54
...
@@ -109,9 +104,10 @@ config RMI4_F54
config RMI4_F55
config RMI4_F55
bool "RMI4 Function 55 (Sensor tuning)"
bool "RMI4 Function 55 (Sensor tuning)"
depends on RMI4_CORE
help
help
Say Y here if you want to add support for RMI4 function 55
Say Y here if you want to add support for RMI4 function 55
Function 55 provides access to the RMI4 touch sensor tuning
Function 55 provides access to the RMI4 touch sensor tuning
mechanism.
mechanism.
endif # RMI_CORE
drivers/input/rmi4/rmi_2d_sensor.c
View file @
d5e6e0fa
...
@@ -144,8 +144,13 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor)
...
@@ -144,8 +144,13 @@ static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor)
int
input_flags
=
0
;
int
input_flags
=
0
;
if
(
sensor
->
report_abs
)
{
if
(
sensor
->
report_abs
)
{
if
(
sensor
->
axis_align
.
swap_axes
)
if
(
sensor
->
axis_align
.
swap_axes
)
{
swap
(
sensor
->
max_x
,
sensor
->
max_y
);
swap
(
sensor
->
max_x
,
sensor
->
max_y
);
swap
(
sensor
->
axis_align
.
clip_x_low
,
sensor
->
axis_align
.
clip_y_low
);
swap
(
sensor
->
axis_align
.
clip_x_high
,
sensor
->
axis_align
.
clip_y_high
);
}
sensor
->
min_x
=
sensor
->
axis_align
.
clip_x_low
;
sensor
->
min_x
=
sensor
->
axis_align
.
clip_x_low
;
if
(
sensor
->
axis_align
.
clip_x_high
)
if
(
sensor
->
axis_align
.
clip_x_high
)
...
...
drivers/input/rmi4/rmi_bus.c
View file @
d5e6e0fa
...
@@ -261,10 +261,10 @@ int __rmi_register_function_handler(struct rmi_function_handler *handler,
...
@@ -261,10 +261,10 @@ int __rmi_register_function_handler(struct rmi_function_handler *handler,
driver
->
probe
=
rmi_function_probe
;
driver
->
probe
=
rmi_function_probe
;
driver
->
remove
=
rmi_function_remove
;
driver
->
remove
=
rmi_function_remove
;
error
=
driver_register
(
&
handler
->
driver
);
error
=
driver_register
(
driver
);
if
(
error
)
{
if
(
error
)
{
pr_err
(
"driver_register() failed for %s, error: %d
\n
"
,
pr_err
(
"driver_register() failed for %s, error: %d
\n
"
,
handler
->
driver
.
name
,
error
);
driver
->
name
,
error
);
return
error
;
return
error
;
}
}
...
...
drivers/input/rmi4/rmi_driver.c
View file @
d5e6e0fa
...
@@ -265,6 +265,19 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
...
@@ -265,6 +265,19 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
return
0
;
return
0
;
}
}
struct
rmi_function
*
rmi_find_function
(
struct
rmi_device
*
rmi_dev
,
u8
number
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
&
rmi_dev
->
dev
);
struct
rmi_function
*
entry
;
list_for_each_entry
(
entry
,
&
data
->
function_list
,
node
)
{
if
(
entry
->
fd
.
function_number
==
number
)
return
entry
;
}
return
NULL
;
}
static
int
suspend_one_function
(
struct
rmi_function
*
fn
)
static
int
suspend_one_function
(
struct
rmi_function
*
fn
)
{
{
struct
rmi_function_handler
*
fh
;
struct
rmi_function_handler
*
fh
;
...
@@ -364,7 +377,7 @@ static void rmi_driver_set_input_name(struct rmi_device *rmi_dev,
...
@@ -364,7 +377,7 @@ static void rmi_driver_set_input_name(struct rmi_device *rmi_dev,
struct
input_dev
*
input
)
struct
input_dev
*
input
)
{
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
&
rmi_dev
->
dev
);
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
&
rmi_dev
->
dev
);
char
*
device_name
=
rmi_f01_get_product_ID
(
data
->
f01_container
);
c
onst
c
har
*
device_name
=
rmi_f01_get_product_ID
(
data
->
f01_container
);
char
*
name
;
char
*
name
;
name
=
devm_kasprintf
(
&
rmi_dev
->
dev
,
GFP_KERNEL
,
name
=
devm_kasprintf
(
&
rmi_dev
->
dev
,
GFP_KERNEL
,
...
@@ -836,7 +849,7 @@ static int rmi_create_function(struct rmi_device *rmi_dev,
...
@@ -836,7 +849,7 @@ static int rmi_create_function(struct rmi_device *rmi_dev,
void
*
ctx
,
const
struct
pdt_entry
*
pdt
)
void
*
ctx
,
const
struct
pdt_entry
*
pdt
)
{
{
struct
device
*
dev
=
&
rmi_dev
->
dev
;
struct
device
*
dev
=
&
rmi_dev
->
dev
;
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
&
rmi_dev
->
dev
);
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
int
*
current_irq_count
=
ctx
;
int
*
current_irq_count
=
ctx
;
struct
rmi_function
*
fn
;
struct
rmi_function
*
fn
;
int
i
;
int
i
;
...
@@ -1040,7 +1053,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
...
@@ -1040,7 +1053,7 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
}
}
if
(
data
->
bootloader_mode
)
if
(
data
->
bootloader_mode
)
dev_warn
(
&
rmi_dev
->
dev
,
"Device in bootloader mode.
\n
"
);
dev_warn
(
dev
,
"Device in bootloader mode.
\n
"
);
data
->
irq_count
=
irq_count
;
data
->
irq_count
=
irq_count
;
data
->
num_of_irq_regs
=
(
data
->
irq_count
+
7
)
/
8
;
data
->
num_of_irq_regs
=
(
data
->
irq_count
+
7
)
/
8
;
...
...
drivers/input/rmi4/rmi_driver.h
View file @
d5e6e0fa
...
@@ -93,6 +93,7 @@ bool rmi_is_physical_driver(struct device_driver *);
...
@@ -93,6 +93,7 @@ bool rmi_is_physical_driver(struct device_driver *);
int
rmi_register_physical_driver
(
void
);
int
rmi_register_physical_driver
(
void
);
void
rmi_unregister_physical_driver
(
void
);
void
rmi_unregister_physical_driver
(
void
);
void
rmi_free_function_list
(
struct
rmi_device
*
rmi_dev
);
void
rmi_free_function_list
(
struct
rmi_device
*
rmi_dev
);
struct
rmi_function
*
rmi_find_function
(
struct
rmi_device
*
rmi_dev
,
u8
number
);
int
rmi_enable_sensor
(
struct
rmi_device
*
rmi_dev
);
int
rmi_enable_sensor
(
struct
rmi_device
*
rmi_dev
);
int
rmi_scan_pdt
(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
int
rmi_scan_pdt
(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
int
(
*
callback
)(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
int
(
*
callback
)(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
...
@@ -104,7 +105,7 @@ int rmi_init_functions(struct rmi_driver_data *data);
...
@@ -104,7 +105,7 @@ int rmi_init_functions(struct rmi_driver_data *data);
int
rmi_initial_reset
(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
int
rmi_initial_reset
(
struct
rmi_device
*
rmi_dev
,
void
*
ctx
,
const
struct
pdt_entry
*
pdt
);
const
struct
pdt_entry
*
pdt
);
char
*
rmi_f01_get_product_ID
(
struct
rmi_function
*
fn
);
c
onst
c
har
*
rmi_f01_get_product_ID
(
struct
rmi_function
*
fn
);
#ifdef CONFIG_RMI4_F34
#ifdef CONFIG_RMI4_F34
int
rmi_f34_create_sysfs
(
struct
rmi_device
*
rmi_dev
);
int
rmi_f34_create_sysfs
(
struct
rmi_device
*
rmi_dev
);
...
...
drivers/input/rmi4/rmi_f01.c
View file @
d5e6e0fa
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of.h>
#include <asm/unaligned.h>
#include "rmi_driver.h"
#include "rmi_driver.h"
#define RMI_PRODUCT_ID_LENGTH 10
#define RMI_PRODUCT_ID_LENGTH 10
...
@@ -54,6 +55,7 @@ struct f01_basic_properties {
...
@@ -54,6 +55,7 @@ struct f01_basic_properties {
u8
product_id
[
RMI_PRODUCT_ID_LENGTH
+
1
];
u8
product_id
[
RMI_PRODUCT_ID_LENGTH
+
1
];
u16
productinfo
;
u16
productinfo
;
u32
firmware_id
;
u32
firmware_id
;
u32
package_id
;
};
};
/* F01 device status bits */
/* F01 device status bits */
...
@@ -220,8 +222,19 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
...
@@ -220,8 +222,19 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
has_build_id_query
=
!!
(
queries
[
0
]
&
BIT
(
1
));
has_build_id_query
=
!!
(
queries
[
0
]
&
BIT
(
1
));
}
}
if
(
has_package_id_query
)
if
(
has_package_id_query
)
{
ret
=
rmi_read_block
(
rmi_dev
,
prod_info_addr
,
queries
,
sizeof
(
__le64
));
if
(
ret
)
{
dev_err
(
&
rmi_dev
->
dev
,
"Failed to read package info: %d
\n
"
,
ret
);
return
ret
;
}
props
->
package_id
=
get_unaligned_le64
(
queries
);
prod_info_addr
++
;
prod_info_addr
++
;
}
if
(
has_build_id_query
)
{
if
(
has_build_id_query
)
{
ret
=
rmi_read_block
(
rmi_dev
,
prod_info_addr
,
queries
,
ret
=
rmi_read_block
(
rmi_dev
,
prod_info_addr
,
queries
,
...
@@ -241,13 +254,90 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
...
@@ -241,13 +254,90 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
return
0
;
return
0
;
}
}
char
*
rmi_f01_get_product_ID
(
struct
rmi_function
*
fn
)
c
onst
c
har
*
rmi_f01_get_product_ID
(
struct
rmi_function
*
fn
)
{
{
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
fn
->
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
fn
->
dev
);
return
f01
->
properties
.
product_id
;
return
f01
->
properties
.
product_id
;
}
}
static
ssize_t
rmi_driver_manufacturer_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
data
->
f01_container
->
dev
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
f01
->
properties
.
manufacturer_id
);
}
static
DEVICE_ATTR
(
manufacturer_id
,
0444
,
rmi_driver_manufacturer_id_show
,
NULL
);
static
ssize_t
rmi_driver_dom_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
data
->
f01_container
->
dev
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
f01
->
properties
.
dom
);
}
static
DEVICE_ATTR
(
date_of_manufacture
,
0444
,
rmi_driver_dom_show
,
NULL
);
static
ssize_t
rmi_driver_product_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
data
->
f01_container
->
dev
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
f01
->
properties
.
product_id
);
}
static
DEVICE_ATTR
(
product_id
,
0444
,
rmi_driver_product_id_show
,
NULL
);
static
ssize_t
rmi_driver_firmware_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
data
->
f01_container
->
dev
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
f01
->
properties
.
firmware_id
);
}
static
DEVICE_ATTR
(
firmware_id
,
0444
,
rmi_driver_firmware_id_show
,
NULL
);
static
ssize_t
rmi_driver_package_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
data
->
f01_container
->
dev
);
u32
package_id
=
f01
->
properties
.
package_id
;
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%04x.%04x
\n
"
,
package_id
&
0xffff
,
(
package_id
>>
16
)
&
0xffff
);
}
static
DEVICE_ATTR
(
package_id
,
0444
,
rmi_driver_package_id_show
,
NULL
);
static
struct
attribute
*
rmi_f01_attrs
[]
=
{
&
dev_attr_manufacturer_id
.
attr
,
&
dev_attr_date_of_manufacture
.
attr
,
&
dev_attr_product_id
.
attr
,
&
dev_attr_firmware_id
.
attr
,
&
dev_attr_package_id
.
attr
,
NULL
};
static
struct
attribute_group
rmi_f01_attr_group
=
{
.
attrs
=
rmi_f01_attrs
,
};
#ifdef CONFIG_OF
#ifdef CONFIG_OF
static
int
rmi_f01_of_probe
(
struct
device
*
dev
,
static
int
rmi_f01_of_probe
(
struct
device
*
dev
,
struct
rmi_device_platform_data
*
pdata
)
struct
rmi_device_platform_data
*
pdata
)
...
@@ -480,9 +570,18 @@ static int rmi_f01_probe(struct rmi_function *fn)
...
@@ -480,9 +570,18 @@ static int rmi_f01_probe(struct rmi_function *fn)
dev_set_drvdata
(
&
fn
->
dev
,
f01
);
dev_set_drvdata
(
&
fn
->
dev
,
f01
);
error
=
sysfs_create_group
(
&
fn
->
rmi_dev
->
dev
.
kobj
,
&
rmi_f01_attr_group
);
if
(
error
)
dev_warn
(
&
fn
->
dev
,
"Failed to create sysfs group: %d
\n
"
,
error
);
return
0
;
return
0
;
}
}
static
void
rmi_f01_remove
(
struct
rmi_function
*
fn
)
{
sysfs_remove_group
(
&
fn
->
rmi_dev
->
dev
.
kobj
,
&
rmi_f01_attr_group
);
}
static
int
rmi_f01_config
(
struct
rmi_function
*
fn
)
static
int
rmi_f01_config
(
struct
rmi_function
*
fn
)
{
{
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
fn
->
dev
);
struct
f01_data
*
f01
=
dev_get_drvdata
(
&
fn
->
dev
);
...
@@ -622,6 +721,7 @@ struct rmi_function_handler rmi_f01_handler = {
...
@@ -622,6 +721,7 @@ struct rmi_function_handler rmi_f01_handler = {
},
},
.
func
=
0x01
,
.
func
=
0x01
,
.
probe
=
rmi_f01_probe
,
.
probe
=
rmi_f01_probe
,
.
remove
=
rmi_f01_remove
,
.
config
=
rmi_f01_config
,
.
config
=
rmi_f01_config
,
.
attention
=
rmi_f01_attention
,
.
attention
=
rmi_f01_attention
,
.
suspend
=
rmi_f01_suspend
,
.
suspend
=
rmi_f01_suspend
,
...
...
drivers/input/rmi4/rmi_f03.c
View file @
d5e6e0fa
...
@@ -175,9 +175,6 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits)
...
@@ -175,9 +175,6 @@ static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits)
int
i
;
int
i
;
int
error
;
int
error
;
if
(
!
rmi_dev
)
return
-
ENODEV
;
if
(
drvdata
->
attn_data
.
data
)
{
if
(
drvdata
->
attn_data
.
data
)
{
/* First grab the data passed by the transport device */
/* First grab the data passed by the transport device */
if
(
drvdata
->
attn_data
.
size
<
ob_len
)
{
if
(
drvdata
->
attn_data
.
size
<
ob_len
)
{
...
...
drivers/input/rmi4/rmi_f34.c
View file @
d5e6e0fa
...
@@ -157,6 +157,9 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
...
@@ -157,6 +157,9 @@ static int rmi_f34_write_blocks(struct f34_data *f34, const void *data,
i
+
1
,
block_count
);
i
+
1
,
block_count
);
data
+=
f34
->
v5
.
block_size
;
data
+=
f34
->
v5
.
block_size
;
f34
->
update_progress
+=
f34
->
v5
.
block_size
;
f34
->
update_status
=
(
f34
->
update_progress
*
100
)
/
f34
->
update_size
;
}
}
return
0
;
return
0
;
...
@@ -174,7 +177,7 @@ static int rmi_f34_write_config(struct f34_data *f34, const void *data)
...
@@ -174,7 +177,7 @@ static int rmi_f34_write_config(struct f34_data *f34, const void *data)
F34_WRITE_CONFIG_BLOCK
);
F34_WRITE_CONFIG_BLOCK
);
}
}
int
rmi_f34_enable_flash
(
struct
f34_data
*
f34
)
static
int
rmi_f34_enable_flash
(
struct
f34_data
*
f34
)
{
{
return
rmi_f34_command
(
f34
,
F34_ENABLE_FLASH_PROG
,
return
rmi_f34_command
(
f34
,
F34_ENABLE_FLASH_PROG
,
F34_ENABLE_WAIT_MS
,
true
);
F34_ENABLE_WAIT_MS
,
true
);
...
@@ -184,9 +187,14 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
...
@@ -184,9 +187,14 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
const
struct
rmi_f34_firmware
*
syn_fw
)
const
struct
rmi_f34_firmware
*
syn_fw
)
{
{
struct
rmi_function
*
fn
=
f34
->
fn
;
struct
rmi_function
*
fn
=
f34
->
fn
;
u32
image_size
=
le32_to_cpu
(
syn_fw
->
image_size
);
u32
config_size
=
le32_to_cpu
(
syn_fw
->
config_size
);
int
ret
;
int
ret
;
if
(
syn_fw
->
image_size
)
{
f34
->
update_progress
=
0
;
f34
->
update_size
=
image_size
+
config_size
;
if
(
image_size
)
{
dev_info
(
&
fn
->
dev
,
"Erasing firmware...
\n
"
);
dev_info
(
&
fn
->
dev
,
"Erasing firmware...
\n
"
);
ret
=
rmi_f34_command
(
f34
,
F34_ERASE_ALL
,
ret
=
rmi_f34_command
(
f34
,
F34_ERASE_ALL
,
F34_ERASE_WAIT_MS
,
true
);
F34_ERASE_WAIT_MS
,
true
);
...
@@ -194,18 +202,18 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
...
@@ -194,18 +202,18 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
return
ret
;
return
ret
;
dev_info
(
&
fn
->
dev
,
"Writing firmware (%d bytes)...
\n
"
,
dev_info
(
&
fn
->
dev
,
"Writing firmware (%d bytes)...
\n
"
,
syn_fw
->
image_size
);
image_size
);
ret
=
rmi_f34_write_firmware
(
f34
,
syn_fw
->
data
);
ret
=
rmi_f34_write_firmware
(
f34
,
syn_fw
->
data
);
if
(
ret
)
if
(
ret
)
return
ret
;
return
ret
;
}
}
if
(
syn_fw
->
config_size
)
{
if
(
config_size
)
{
/*
/*
* We only need to erase config if we haven't updated
* We only need to erase config if we haven't updated
* firmware.
* firmware.
*/
*/
if
(
!
syn_fw
->
image_size
)
{
if
(
!
image_size
)
{
dev_info
(
&
fn
->
dev
,
"Erasing config...
\n
"
);
dev_info
(
&
fn
->
dev
,
"Erasing config...
\n
"
);
ret
=
rmi_f34_command
(
f34
,
F34_ERASE_CONFIG
,
ret
=
rmi_f34_command
(
f34
,
F34_ERASE_CONFIG
,
F34_ERASE_WAIT_MS
,
true
);
F34_ERASE_WAIT_MS
,
true
);
...
@@ -214,9 +222,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
...
@@ -214,9 +222,8 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
}
}
dev_info
(
&
fn
->
dev
,
"Writing config (%d bytes)...
\n
"
,
dev_info
(
&
fn
->
dev
,
"Writing config (%d bytes)...
\n
"
,
syn_fw
->
config_size
);
config_size
);
ret
=
rmi_f34_write_config
(
f34
,
ret
=
rmi_f34_write_config
(
f34
,
&
syn_fw
->
data
[
image_size
]);
&
syn_fw
->
data
[
syn_fw
->
image_size
]);
if
(
ret
)
if
(
ret
)
return
ret
;
return
ret
;
}
}
...
@@ -224,21 +231,23 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
...
@@ -224,21 +231,23 @@ static int rmi_f34_flash_firmware(struct f34_data *f34,
return
0
;
return
0
;
}
}
int
rmi_f34_update_firmware
(
struct
f34_data
*
f34
,
const
struct
firmware
*
fw
)
static
int
rmi_f34_update_firmware
(
struct
f34_data
*
f34
,
const
struct
firmware
*
fw
)
{
{
const
struct
rmi_f34_firmware
*
syn_fw
;
const
struct
rmi_f34_firmware
*
syn_fw
=
(
const
struct
rmi_f34_firmware
*
)
fw
->
data
;
u32
image_size
=
le32_to_cpu
(
syn_fw
->
image_size
);
u32
config_size
=
le32_to_cpu
(
syn_fw
->
config_size
);
int
ret
;
int
ret
;
syn_fw
=
(
const
struct
rmi_f34_firmware
*
)
fw
->
data
;
BUILD_BUG_ON
(
offsetof
(
struct
rmi_f34_firmware
,
data
)
!=
BUILD_BUG_ON
(
offsetof
(
struct
rmi_f34_firmware
,
data
)
!=
F34_FW_IMAGE_OFFSET
);
F34_FW_IMAGE_OFFSET
);
rmi_dbg
(
RMI_DEBUG_FN
,
&
f34
->
fn
->
dev
,
rmi_dbg
(
RMI_DEBUG_FN
,
&
f34
->
fn
->
dev
,
"FW size:%d, checksum:%08x, image_size:%d, config_size:%d
\n
"
,
"FW size:%
z
d, checksum:%08x, image_size:%d, config_size:%d
\n
"
,
(
int
)
fw
->
size
,
fw
->
size
,
le32_to_cpu
(
syn_fw
->
checksum
),
le32_to_cpu
(
syn_fw
->
checksum
),
le32_to_cpu
(
syn_fw
->
image_size
),
image_size
,
config_size
);
le32_to_cpu
(
syn_fw
->
config_size
));
rmi_dbg
(
RMI_DEBUG_FN
,
&
f34
->
fn
->
dev
,
rmi_dbg
(
RMI_DEBUG_FN
,
&
f34
->
fn
->
dev
,
"FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x
\n
"
,
"FW bootloader_id:%02x, product_id:%.*s, info: %02x%02x
\n
"
,
...
@@ -246,27 +255,25 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
...
@@ -246,27 +255,25 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
(
int
)
sizeof
(
syn_fw
->
product_id
),
syn_fw
->
product_id
,
(
int
)
sizeof
(
syn_fw
->
product_id
),
syn_fw
->
product_id
,
syn_fw
->
product_info
[
0
],
syn_fw
->
product_info
[
1
]);
syn_fw
->
product_info
[
0
],
syn_fw
->
product_info
[
1
]);
if
(
syn_fw
->
image_size
&&
if
(
image_size
&&
image_size
!=
f34
->
v5
.
fw_blocks
*
f34
->
v5
.
block_size
)
{
syn_fw
->
image_size
!=
f34
->
v5
.
fw_blocks
*
f34
->
v5
.
block_size
)
{
dev_err
(
&
f34
->
fn
->
dev
,
dev_err
(
&
f34
->
fn
->
dev
,
"Bad firmware image: fw size %d, expected %d
\n
"
,
"Bad firmware image: fw size %d, expected %d
\n
"
,
syn_fw
->
image_size
,
image_size
,
f34
->
v5
.
fw_blocks
*
f34
->
v5
.
block_size
);
f34
->
v5
.
fw_blocks
*
f34
->
v5
.
block_size
);
ret
=
-
EILSEQ
;
ret
=
-
EILSEQ
;
goto
out
;
goto
out
;
}
}
if
(
syn_fw
->
config_size
&&
if
(
config_size
&&
syn_fw
->
config_size
!=
f34
->
v5
.
config_blocks
*
f34
->
v5
.
block_size
)
{
config_size
!=
f34
->
v5
.
config_blocks
*
f34
->
v5
.
block_size
)
{
dev_err
(
&
f34
->
fn
->
dev
,
dev_err
(
&
f34
->
fn
->
dev
,
"Bad firmware image: config size %d, expected %d
\n
"
,
"Bad firmware image: config size %d, expected %d
\n
"
,
syn_fw
->
config_size
,
config_size
,
f34
->
v5
.
config_blocks
*
f34
->
v5
.
block_size
);
f34
->
v5
.
config_blocks
*
f34
->
v5
.
block_size
);
ret
=
-
EILSEQ
;
ret
=
-
EILSEQ
;
goto
out
;
goto
out
;
}
}
if
(
syn_fw
->
image_size
&&
!
syn_fw
->
config_size
)
{
if
(
image_size
&&
!
config_size
)
{
dev_err
(
&
f34
->
fn
->
dev
,
"Bad firmware image: no config data
\n
"
);
dev_err
(
&
f34
->
fn
->
dev
,
"Bad firmware image: no config data
\n
"
);
ret
=
-
EILSEQ
;
ret
=
-
EILSEQ
;
goto
out
;
goto
out
;
...
@@ -283,6 +290,63 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
...
@@ -283,6 +290,63 @@ int rmi_f34_update_firmware(struct f34_data *f34, const struct firmware *fw)
return
ret
;
return
ret
;
}
}
static
int
rmi_f34_status
(
struct
rmi_function
*
fn
)
{
struct
f34_data
*
f34
=
dev_get_drvdata
(
&
fn
->
dev
);
/*
* The status is the percentage complete, or once complete,
* zero for success or a negative return code.
*/
return
f34
->
update_status
;
}
static
ssize_t
rmi_driver_bootloader_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
rmi_function
*
fn
=
data
->
f34_container
;
struct
f34_data
*
f34
;
if
(
fn
)
{
f34
=
dev_get_drvdata
(
&
fn
->
dev
);
if
(
f34
->
bl_version
==
5
)
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%c%c
\n
"
,
f34
->
bootloader_id
[
0
],
f34
->
bootloader_id
[
1
]);
else
return
scnprintf
(
buf
,
PAGE_SIZE
,
"V%d.%d
\n
"
,
f34
->
bootloader_id
[
1
],
f34
->
bootloader_id
[
0
]);
}
return
0
;
}
static
DEVICE_ATTR
(
bootloader_id
,
0444
,
rmi_driver_bootloader_id_show
,
NULL
);
static
ssize_t
rmi_driver_configuration_id_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
struct
rmi_function
*
fn
=
data
->
f34_container
;
struct
f34_data
*
f34
;
if
(
fn
)
{
f34
=
dev_get_drvdata
(
&
fn
->
dev
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%s
\n
"
,
f34
->
configuration_id
);
}
return
0
;
}
static
DEVICE_ATTR
(
configuration_id
,
0444
,
rmi_driver_configuration_id_show
,
NULL
);
static
int
rmi_firmware_update
(
struct
rmi_driver_data
*
data
,
static
int
rmi_firmware_update
(
struct
rmi_driver_data
*
data
,
const
struct
firmware
*
fw
)
const
struct
firmware
*
fw
)
{
{
...
@@ -346,7 +410,13 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
...
@@ -346,7 +410,13 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
else
else
ret
=
rmi_f34_update_firmware
(
f34
,
fw
);
ret
=
rmi_f34_update_firmware
(
f34
,
fw
);
dev_info
(
&
f34
->
fn
->
dev
,
"Firmware update complete, status:%d
\n
"
,
ret
);
if
(
ret
)
{
f34
->
update_status
=
ret
;
dev_err
(
&
f34
->
fn
->
dev
,
"Firmware update failed, status: %d
\n
"
,
ret
);
}
else
{
dev_info
(
&
f34
->
fn
->
dev
,
"Firmware update complete
\n
"
);
}
rmi_disable_irq
(
rmi_dev
,
false
);
rmi_disable_irq
(
rmi_dev
,
false
);
...
@@ -377,9 +447,6 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
...
@@ -377,9 +447,6 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
return
ret
;
return
ret
;
}
}
static
int
rmi_firmware_update
(
struct
rmi_driver_data
*
data
,
const
struct
firmware
*
fw
);
static
ssize_t
rmi_driver_update_fw_store
(
struct
device
*
dev
,
static
ssize_t
rmi_driver_update_fw_store
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
struct
device_attribute
*
dattr
,
const
char
*
buf
,
size_t
count
)
const
char
*
buf
,
size_t
count
)
...
@@ -414,8 +481,27 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev,
...
@@ -414,8 +481,27 @@ static ssize_t rmi_driver_update_fw_store(struct device *dev,
static
DEVICE_ATTR
(
update_fw
,
0200
,
NULL
,
rmi_driver_update_fw_store
);
static
DEVICE_ATTR
(
update_fw
,
0200
,
NULL
,
rmi_driver_update_fw_store
);
static
ssize_t
rmi_driver_update_fw_status_show
(
struct
device
*
dev
,
struct
device_attribute
*
dattr
,
char
*
buf
)
{
struct
rmi_driver_data
*
data
=
dev_get_drvdata
(
dev
);
int
update_status
=
0
;
if
(
data
->
f34_container
)
update_status
=
rmi_f34_status
(
data
->
f34_container
);
return
scnprintf
(
buf
,
PAGE_SIZE
,
"%d
\n
"
,
update_status
);
}
static
DEVICE_ATTR
(
update_fw_status
,
0444
,
rmi_driver_update_fw_status_show
,
NULL
);
static
struct
attribute
*
rmi_firmware_attrs
[]
=
{
static
struct
attribute
*
rmi_firmware_attrs
[]
=
{
&
dev_attr_bootloader_id
.
attr
,
&
dev_attr_configuration_id
.
attr
,
&
dev_attr_update_fw
.
attr
,
&
dev_attr_update_fw
.
attr
,
&
dev_attr_update_fw_status
.
attr
,
NULL
NULL
};
};
...
@@ -441,8 +527,6 @@ static int rmi_f34_probe(struct rmi_function *fn)
...
@@ -441,8 +527,6 @@ static int rmi_f34_probe(struct rmi_function *fn)
/* v5 code only supported version 0, try V7 probe */
/* v5 code only supported version 0, try V7 probe */
if
(
version
>
0
)
if
(
version
>
0
)
return
rmi_f34v7_probe
(
f34
);
return
rmi_f34v7_probe
(
f34
);
else
if
(
version
!=
0
)
return
-
ENODEV
;
f34
->
bl_version
=
5
;
f34
->
bl_version
=
5
;
...
...
drivers/input/rmi4/rmi_f34.h
View file @
d5e6e0fa
...
@@ -301,6 +301,10 @@ struct f34_data {
...
@@ -301,6 +301,10 @@ struct f34_data {
unsigned
char
bootloader_id
[
5
];
unsigned
char
bootloader_id
[
5
];
unsigned
char
configuration_id
[
CONFIG_ID_SIZE
*
2
+
1
];
unsigned
char
configuration_id
[
CONFIG_ID_SIZE
*
2
+
1
];
int
update_status
;
int
update_progress
;
int
update_size
;
union
{
union
{
struct
f34v5_data
v5
;
struct
f34v5_data
v5
;
struct
f34v7_data
v7
;
struct
f34v7_data
v7
;
...
...
drivers/input/rmi4/rmi_f34v7.c
View file @
d5e6e0fa
...
@@ -588,6 +588,7 @@ static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
...
@@ -588,6 +588,7 @@ static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
u16
block_count
;
u16
block_count
;
block_count
=
f34
->
v7
.
img
.
ui_firmware
.
size
/
f34
->
v7
.
block_size
;
block_count
=
f34
->
v7
.
img
.
ui_firmware
.
size
/
f34
->
v7
.
block_size
;
f34
->
update_size
+=
block_count
;
if
(
block_count
!=
f34
->
v7
.
blkcount
.
ui_firmware
)
{
if
(
block_count
!=
f34
->
v7
.
blkcount
.
ui_firmware
)
{
dev_err
(
&
f34
->
fn
->
dev
,
dev_err
(
&
f34
->
fn
->
dev
,
...
@@ -604,6 +605,7 @@ static int rmi_f34v7_check_ui_config_size(struct f34_data *f34)
...
@@ -604,6 +605,7 @@ static int rmi_f34v7_check_ui_config_size(struct f34_data *f34)
u16
block_count
;
u16
block_count
;
block_count
=
f34
->
v7
.
img
.
ui_config
.
size
/
f34
->
v7
.
block_size
;
block_count
=
f34
->
v7
.
img
.
ui_config
.
size
/
f34
->
v7
.
block_size
;
f34
->
update_size
+=
block_count
;
if
(
block_count
!=
f34
->
v7
.
blkcount
.
ui_config
)
{
if
(
block_count
!=
f34
->
v7
.
blkcount
.
ui_config
)
{
dev_err
(
&
f34
->
fn
->
dev
,
"UI config size mismatch
\n
"
);
dev_err
(
&
f34
->
fn
->
dev
,
"UI config size mismatch
\n
"
);
...
@@ -618,6 +620,7 @@ static int rmi_f34v7_check_dp_config_size(struct f34_data *f34)
...
@@ -618,6 +620,7 @@ static int rmi_f34v7_check_dp_config_size(struct f34_data *f34)
u16
block_count
;
u16
block_count
;
block_count
=
f34
->
v7
.
img
.
dp_config
.
size
/
f34
->
v7
.
block_size
;
block_count
=
f34
->
v7
.
img
.
dp_config
.
size
/
f34
->
v7
.
block_size
;
f34
->
update_size
+=
block_count
;
if
(
block_count
!=
f34
->
v7
.
blkcount
.
dp_config
)
{
if
(
block_count
!=
f34
->
v7
.
blkcount
.
dp_config
)
{
dev_err
(
&
f34
->
fn
->
dev
,
"Display config size mismatch
\n
"
);
dev_err
(
&
f34
->
fn
->
dev
,
"Display config size mismatch
\n
"
);
...
@@ -632,6 +635,8 @@ static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
...
@@ -632,6 +635,8 @@ static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
u16
block_count
;
u16
block_count
;
block_count
=
f34
->
v7
.
img
.
guest_code
.
size
/
f34
->
v7
.
block_size
;
block_count
=
f34
->
v7
.
img
.
guest_code
.
size
/
f34
->
v7
.
block_size
;
f34
->
update_size
+=
block_count
;
if
(
block_count
!=
f34
->
v7
.
blkcount
.
guest_code
)
{
if
(
block_count
!=
f34
->
v7
.
blkcount
.
guest_code
)
{
dev_err
(
&
f34
->
fn
->
dev
,
"Guest code size mismatch
\n
"
);
dev_err
(
&
f34
->
fn
->
dev
,
"Guest code size mismatch
\n
"
);
return
-
EINVAL
;
return
-
EINVAL
;
...
@@ -645,6 +650,7 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
...
@@ -645,6 +650,7 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
u16
block_count
;
u16
block_count
;
block_count
=
f34
->
v7
.
img
.
bl_config
.
size
/
f34
->
v7
.
block_size
;
block_count
=
f34
->
v7
.
img
.
bl_config
.
size
/
f34
->
v7
.
block_size
;
f34
->
update_size
+=
block_count
;
if
(
block_count
!=
f34
->
v7
.
blkcount
.
bl_config
)
{
if
(
block_count
!=
f34
->
v7
.
blkcount
.
bl_config
)
{
dev_err
(
&
f34
->
fn
->
dev
,
"Bootloader config size mismatch
\n
"
);
dev_err
(
&
f34
->
fn
->
dev
,
"Bootloader config size mismatch
\n
"
);
...
@@ -881,6 +887,9 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
...
@@ -881,6 +887,9 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
block_ptr
+=
(
transfer
*
f34
->
v7
.
block_size
);
block_ptr
+=
(
transfer
*
f34
->
v7
.
block_size
);
remaining
-=
transfer
;
remaining
-=
transfer
;
f34
->
update_progress
+=
transfer
;
f34
->
update_status
=
(
f34
->
update_progress
*
100
)
/
f34
->
update_size
;
}
while
(
remaining
);
}
while
(
remaining
);
return
0
;
return
0
;
...
@@ -1191,6 +1200,8 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
...
@@ -1191,6 +1200,8 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
rmi_f34v7_read_queries_bl_version
(
f34
);
rmi_f34v7_read_queries_bl_version
(
f34
);
f34
->
v7
.
image
=
fw
->
data
;
f34
->
v7
.
image
=
fw
->
data
;
f34
->
update_progress
=
0
;
f34
->
update_size
=
0
;
ret
=
rmi_f34v7_parse_image_info
(
f34
);
ret
=
rmi_f34v7_parse_image_info
(
f34
);
if
(
ret
<
0
)
if
(
ret
<
0
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment